openapi: 3.0.3
info:
  title: Repzo API - AI Object Detection Dataset
  version: 1.0.0
  description: |
    Training **datasets** for the AI object-detection pipeline — a named set of
    `dataset_labels` (see `ai-object-detection-label`) plus a `default_model` and
    `default_model_version` used when training/predicting.

    **Who calls it.** Back-office admins (AI configuration). Scoped by
    `company_namespace[]`; soft-delete via `disabled`. `PATCH` is not allowed
    (400). Part of the `ai-object-detection-*` family.

    **Key relationships.** `dataset_labels[]` → `ai-object-detection-label`;
    `default_model` → `ai-object-detection-model`; `default_model_version` →
    `ai-object-detection-model-version`. Tasks join a dataset through their own
    `task_dataset[]` (see `ai-object-detection-task`), and a model version pins
    one or more datasets when it is created. Any of the three refs can be
    populated with `populatedKeys[]`; the populated document is returned under
    `<field>_populated` while the original field keeps the id.

    **The `latest` sentinel.** `default_model_version` may be sent as a version
    `_id`, or as `latest` / `""` / `null`. The sentinel is normalized to `null`
    server-side (never cast to an ObjectId) and `null` resolves to the model's
    `current_model_version` at inference time — which auto-advances to the
    newest trained version. Sending the sentinel on update actively clears a
    previously pinned version. `name` is unique per namespace.
servers:
  - url: https://sv.api.repzo.me
security:
  - ApiKeyAuth: []
  - JwtAuth: []
paths:
  /ai-object-detection-dataset:
    get:
      summary: Find datasets
      operationId: findAiObjectDetectionDatasets
      parameters:
        - in: query
          name: _id
          description: "Filter by dataset `_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` (one or many).
          schema:
            oneOf:
              - type: string
              - type: array
                items: { type: string }
        - in: query
          name: dataset_labels
          description: Datasets containing any of the given label ids.
          schema:
            oneOf:
              - type: string
              - type: array
                items: { type: string }
        - in: query
          name: default_model
          description: Filter by default model id (one or many).
          schema:
            oneOf:
              - type: string
              - type: array
                items: { type: string }
        - in: query
          name: default_model_version
          description: Filter by pinned default model-version id (one or many).
          schema:
            oneOf:
              - type: string
              - type: array
                items: { type: string }
        - in: query
          name: search
          description: Case-insensitive regex match on `name` (whitespace matches any run of characters).
          schema: { type: string }
          example: shelf
        - in: query
          name: disabled
          description: Include soft-deleted datasets. Defaults to `false`.
          schema: { type: boolean, default: false }
        - in: query
          name: from_updatedAt
          description: Only datasets with `updatedAt` on/after this Unix timestamp (ms), start of that day in the caller's timezone.
          schema: { type: number }
        - in: query
          name: to_updatedAt
          description: Only datasets with `updatedAt` on/before this Unix timestamp (ms), end of that day.
          schema: { type: number }
        - in: query
          name: from_createdAt
          description: Only datasets with `createdAt` on/after this Unix timestamp (ms).
          schema: { type: number }
        - in: query
          name: to_createdAt
          description: Only datasets with `createdAt` on/before this Unix timestamp (ms).
          schema: { type: number }
        - in: query
          name: populatedKeys
          description: "Refs to populate. Each is returned under `<field>_populated`; the original field keeps the id(s). Encode as `?populatedKeys[]=default_model`."
          schema:
            type: array
            items:
              type: string
              enum: [dataset_labels, default_model, default_model_version]
        - in: query
          name: per_page
          schema: { type: integer, minimum: 1, maximum: 50000 }
          example: 20
        - in: query
          name: page
          schema: { type: integer, minimum: 1 }
          example: 1
        - in: query
          name: sort
          description: Field to sort by. Defaults to `_id`.
          schema: { type: string }
        - in: query
          name: sortPageOrder
          description: Sort direction. Defaults to descending.
          schema: { type: string, enum: [asc, dsc] }
      responses:
        "200":
          {
            description: Paginated list of datasets.,
            content:
              {
                application/json:
                  {
                    schema:
                      { $ref: "#/components/schemas/OdDatasetFindResult" },
                  },
              },
          }
    post:
      summary: Create a dataset
      operationId: createAiObjectDetectionDataset
      requestBody:
        {
          required: true,
          content:
            {
              application/json:
                {
                  schema: { $ref: "#/components/schemas/OdDatasetCreateBody" },
                },
            },
        }
      responses:
        "201":
          {
            description: The created dataset.,
            content:
              {
                application/json:
                  { schema: { $ref: "#/components/schemas/OdDatasetSchema" } },
              },
          }
  /ai-object-detection-dataset/{id}:
    get:
      summary: Get a dataset by id
      operationId: getAiObjectDetectionDataset
      parameters:
        - { in: path, name: id, required: true, schema: { type: string } }
        - in: query
          name: populatedKeys
          description: Refs to populate (same semantics as on find).
          schema:
            type: array
            items:
              type: string
              enum: [dataset_labels, default_model, default_model_version]
      responses:
        "200":
          {
            description: The dataset.,
            content:
              {
                application/json:
                  { schema: { $ref: "#/components/schemas/OdDatasetSchema" } },
              },
          }
        "400": { description: No dataset with that id. }
    put:
      summary: Update a dataset
      description: 'Full-document style update (`updateOne` with validators). The `latest` / `""` / `null` sentinel on `default_model_version` clears the pin.'
      operationId: updateAiObjectDetectionDataset
      parameters:
        [{ in: path, name: id, required: true, schema: { type: string } }]
      requestBody:
        {
          required: true,
          content:
            {
              application/json:
                {
                  schema: { $ref: "#/components/schemas/OdDatasetUpdateBody" },
                },
            },
        }
      responses:
        "200":
          {
            description: The dataset after the update.,
            content:
              {
                application/json:
                  { schema: { $ref: "#/components/schemas/OdDatasetSchema" } },
              },
          }
        "404": { description: No dataset with that id. }
    delete:
      summary: Soft-delete a dataset
      description: "Sets `disabled: true`."
      operationId: removeAiObjectDetectionDataset
      parameters:
        [{ in: path, name: id, required: true, schema: { type: string } }]
      responses:
        "200":
          {
            description: The dataset after soft-deletion.,
            content:
              {
                application/json:
                  { schema: { $ref: "#/components/schemas/OdDatasetSchema" } },
              },
          }
components:
  securitySchemes:
    ApiKeyAuth:
      {
        type: apiKey,
        in: header,
        name: api-key,
        description: "Server-issued API key. Also `x-api-key` header or `?apiKey=` query.",
      }
    JwtAuth:
      {
        type: apiKey,
        in: header,
        name: Authorization,
        description: "Raw JWT — no `Bearer ` prefix. From `POST /authenticate`.",
      }
  schemas:
    OdDatasetSchema:
      type: object
      properties:
        _id: { type: string }
        name: { type: string, description: Unique per namespace. }
        dataset_labels:
          {
            type: array,
            items: { type: string },
            description: Label ids in the dataset.,
          }
        default_model:
          { type: string, description: "`ai-object-detection-model` id." }
        default_model_version:
          {
            type: string,
            nullable: true,
            description: "Pinned default version, or `null` = latest (resolves to the model's `current_model_version` at inference time).",
          }
        dataset_labels_populated:
          type: array
          description: "Present when `populatedKeys[]` includes `dataset_labels` — the `ai-object-detection-label` documents."
          items: { type: object, additionalProperties: true }
        default_model_populated:
          type: object
          nullable: true
          additionalProperties: true
          description: "Present when `populatedKeys[]` includes `default_model` — the `ai-object-detection-model` document."
        default_model_version_populated:
          type: object
          nullable: true
          additionalProperties: true
          description: "Present when `populatedKeys[]` includes `default_model_version` — the `ai-object-detection-model-version` document."
        disabled: { type: boolean }
        company_namespace: { type: array, items: { type: string } }
        createdAt: { type: string, format: date-time }
        updatedAt: { type: string, format: date-time }
    OdDatasetCreateBody:
      type: object
      description: "Body for creating a dataset. The tenant key (`company_namespace`) is optional for SDK callers and is otherwise injected from the caller's session."
      required: [name, dataset_labels]
      properties:
        name: { type: string }
        dataset_labels:
          {
            type: array,
            items: { type: string },
            description: "`ai-object-detection-label` ids.",
          }
        default_model: { type: string }
        default_model_version:
          {
            type: string,
            nullable: true,
            description: 'Pinned version `_id`; or send `latest` / `""` / `null` to track the model''s `current_model_version` (the sentinel is normalized to `null` server-side, never cast to an ObjectId).',
          }
        company_namespace:
          type: array
          items: { type: string }
          description: Optional tenant namespace override for SDK callers.
    OdDatasetUpdateBody:
      type: object
      description: "Body for updating a dataset. `company_namespace` is derived from the caller's session — do not send it. Set `disabled: true` to soft-delete."
      properties:
        name: { type: string }
        dataset_labels: { type: array, items: { type: string } }
        default_model: { type: string }
        default_model_version:
          {
            type: string,
            nullable: true,
            description: 'Pinned version `_id`; `latest` / `""` / `null` actively clears a previously pinned version.',
          }
        disabled: { type: boolean }
    OdDatasetFindResult:
      type: object
      properties:
        data:
          {
            type: array,
            items: { $ref: "#/components/schemas/OdDatasetSchema" },
          }
        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 }
