openapi: 3.0.3
info:
  title: Repzo API - AI Object Detection Segment
  version: 1.0.0
  description: |
    **Segments** are reusable named label sets for the Object Detection
    business layer — "our brand", "competitor X", "energy drinks". They are
    the SUBJECTS of share-of-shelf metrics
    (`/ai-object-detection-metric`, `type: share_of_shelf`): a metric adds
    one or more segment rows and may override a segment's labels for that
    metric only; rows without an override follow the segment's own labels —
    editing the segment updates every such metric on its next evaluation.

    `labels` is optional — an empty segment is a named placeholder whose
    metrics must supply their own override.

    **Referential guard.** Deleting a segment is BLOCKED while an active
    metric still references it (the error names the metrics); remove it from
    those metrics first.

    **Multi-tenancy & lifecycle.** Scoped by `company_namespace` (injected
    from the caller's token), soft-deleted via `disabled: true`; the name is
    unique per namespace among live segments. The server stamps `creator`
    (on create) and `editor` (on update / delete) from the caller's token.
    `PUT` re-validates the whole body, so `name` must be present and non-blank
    on every update. `PATCH` is not allowed (400). References
    `ai-object-detection-label`. Admin-facing.
servers:
  - url: https://sv.api.repzo.me
security:
  - ApiKeyAuth: []
  - JwtAuth: []
paths:
  /ai-object-detection-segment:
    get:
      summary: List segments
      operationId: findAiObjectDetectionSegment
      parameters:
        - in: query
          name: _id
          description: Filter by segment `_id`. Pass once or as `?_id[]=...` for multiple.
          schema:
            oneOf:
              - type: string
              - type: array
                items: { type: string }
        - in: query
          name: name
          description: Exact-match on `name` (single value or list).
          schema:
            oneOf:
              - type: string
              - type: array
                items: { type: string }
        - in: query
          name: search
          description: Case-insensitive substring / regex match on `name`.
          schema: { type: string }
        - in: query
          name: labels
          description: Segments containing any of these label ids.
          schema:
            oneOf:
              - type: string
              - type: array
                items: { type: string }
        - in: query
          name: disabled
          description: "`true` returns only soft-deleted segments, `false` only live ones. Omitted returns both."
          schema: { type: boolean }
        - in: query
          name: populatedKeys
          description: "Embed refs. Supported: `labels` (adds `labels_populated`)."
          schema:
            type: array
            items:
              type: string
              enum: [labels]
        - in: query
          name: from_updatedAt
          description: Return segments with `updatedAt` on/after this Unix timestamp (ms); snapped to start of day unless `exact_time=true`.
          schema: { type: number }
        - in: query
          name: to_updatedAt
          description: Return segments with `updatedAt` on/before this Unix timestamp (ms).
          schema: { type: number }
        - in: query
          name: from_createdAt
          description: Return segments with `createdAt` on/after this Unix timestamp (ms).
          schema: { type: number }
        - in: query
          name: to_createdAt
          description: Return segments with `createdAt` on/before this Unix timestamp (ms).
          schema: { type: number }
        - in: query
          name: per_page
          description: Page size (capped by the server's pagination max).
          schema: { type: integer, minimum: 1 }
          example: 20
        - in: query
          name: page
          description: 1-based page number.
          schema: { type: integer, minimum: 1 }
          example: 1
        - in: query
          name: sort
          description: Field to sort by. Defaults to `_id`.
          schema: { type: string, default: _id }
        - in: query
          name: sortPageOrder
          description: Sort direction. Defaults to descending.
          schema: { type: string, enum: [asc, dsc], default: dsc }
      responses:
        "200":
          description: Paginated segments.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SegmentFindResult"
    post:
      summary: Create a segment
      operationId: createAiObjectDetectionSegment
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SegmentCreateBody"
      responses:
        "201":
          description: The created segment (with the server-stamped `creator`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Segment"
        "400":
          description: Missing / blank name, or the name already exists in the namespace.
  /ai-object-detection-segment/{id}:
    get:
      summary: Get a segment
      operationId: getAiObjectDetectionSegment
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        "200":
          description: The segment document.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Segment"
        "400":
          description: No segment with that id in the caller's namespace.
    put:
      summary: Update a segment
      description: "Label edits propagate to every metric row WITHOUT an override on its next evaluation. `name` is re-validated (required, non-blank). Stamps `editor`."
      operationId: updateAiObjectDetectionSegment
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SegmentUpdateBody"
      responses:
        "200":
          description: The updated segment.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Segment"
        "400":
          description: Missing / blank name.
        "404":
          description: No segment with that id in the caller's namespace.
    delete:
      summary: Soft-delete a segment (guarded)
      description: "Sets `disabled: true` and stamps `editor` — REJECTED while any active metric references the segment (the 400 lists the metric names)."
      operationId: removeAiObjectDetectionSegment
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        "200":
          description: The disabled segment.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Segment"
        "400":
          description: The segment is still referenced by metrics.
        "404":
          description: No segment with that id in the caller's namespace.
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: api-key
      description: |
        Server-issued API key. Also accepted via the `x-api-key` header or the
        `?apiKey=` query parameter as fallbacks.
    JwtAuth:
      type: apiKey
      in: header
      name: Authorization
      description: |
        Raw JWT in the `Authorization` header — **no `Bearer ` prefix**.
        Obtained from `POST /authenticate` (admin / rep / client login).
  schemas:
    UserStamp:
      type: object
      description: Creator / editor stamp derived from the caller's token.
      properties:
        _id: { type: string }
        type:
          type: string
          enum: [admin, rep, tenant, client]
        name: { type: string }
        admin: { type: string }
        rep: { type: string }
        tenant: { type: string }
        client: { type: string }
    SegmentCreateBody:
      type: object
      description: "Body for creating a segment. `company_namespace` is optional for SDK callers and otherwise injected from the caller's session."
      required: [name]
      properties:
        name:
          type: string
          description: Unique per namespace among live segments. Must be non-blank.
        description: { type: string }
        labels:
          type: array
          items: { type: string }
          description: Member label ids (optional — empty = placeholder).
        company_namespace:
          type: array
          items: { type: string }
          description: Optional tenant namespace override for SDK callers.
    SegmentUpdateBody:
      type: object
      description: "Body for updating a segment. `company_namespace` is derived from the caller's session — do not send it. `name` is required on every PUT."
      required: [name]
      properties:
        name:
          type: string
          description: Unique per namespace among live segments. Must be non-blank.
        description: { type: string }
        labels:
          type: array
          items: { type: string }
        disabled:
          type: boolean
          description: "Soft-delete flag — set `true` to disable via update (bypasses the metric guard on DELETE)."
    Segment:
      type: object
      properties:
        _id: { type: string }
        disabled: { type: boolean }
        name: { type: string }
        description: { type: string }
        labels:
          type: array
          items: { type: string }
        creator:
          $ref: "#/components/schemas/UserStamp"
        editor:
          $ref: "#/components/schemas/UserStamp"
        company_namespace:
          type: array
          items: { type: string }
          description: Tenant key (server-injected).
        createdAt: { type: string, format: date-time }
        updatedAt: { type: string, format: date-time }
    SegmentFindResult:
      type: object
      description: Standard paginated result envelope.
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Segment"
        total_result: { type: number }
        current_count: { type: number }
        total_pages: { type: number }
        current_page: { type: number }
        per_page: { type: number }
        first_page_url: { type: string }
        last_page_url: { type: string }
        next_page_url: { type: string, nullable: true }
        prev_page_url: { type: string, nullable: true }
        path: { type: string }
