openapi: 3.1.0
info:
  title: Bigscreen Admin API
  version: '0'
  summary: Internal admin API for Arda, factory tooling, and integration tests.
  description: |
    The **Bigscreen Admin API** powers Arda (the internal admin webapp), the factory floor
    (CNC machines, Fusion 360, pose-analysis tools), and the integration test suite. It is
    not a public API — production access is gated by an EC2 security group that admits only
    Bigscreen office and factory IPs.

    273 endpoints across four auth domains (`/admin/*`, `/fusion/*`, `/cnc/*`, `/pose/*`)
    plus an unauthenticated `/health` probe. Endpoints are grouped into 19 tags.

    ## Auth at a glance

    Every `/admin/*` request needs **two** headers:

    | Header | Value |
    |--------|-------|
    | `Authorization` | `Bearer <admin-api key>` |
    | `x-access-token` | A JWT obtained from the public auth API |

    And the access token's account must hold one of the **access policies** the endpoint
    requires (`Admin`, `Moderator`, `Fabricator`, `Inventory`, etc.). Policies are listed
    under each endpoint's `x-access-policies` extension.

    `/fusion/*`, `/cnc/*`, and `/pose/*` use their own per-domain bearer keys — Fusion has
    no per-user access token (LB-gated), CNC and Pose require an operator-account JWT.

    See `components.securitySchemes` below for the four API identities (`admin-api`,
    `fusion-api`, `cnc-machine-api`, `pose-api`) and the relationship between the
    bearer keys, access tokens, and per-endpoint access policies.

    ## Spec layout

    The OpenAPI source is split per handler module under `docs/openapi/modules/*.yaml`,
    joined by `$ref` from this root file. The bundled artifact at
    `docs/openapi/admin-api.bundled.yaml` is what the Scalar renderer reads — regenerate
    it with `npx -y @redocly/cli@latest bundle docs/openapi/admin-api.yaml -o docs/openapi/admin-api.bundled.yaml`
    after any edit.
servers:
  - url: http://localhost:3999
    description: Local dev (started by `yarn dev:admin-api`)
  - url: https://admin-api.internal.bigscreenvr.com
    description: Production (network-restricted)
security:
  - adminApiKey: []
    adminAccessToken: []
tags:
  - name: Health
    description: |
      Unauthenticated liveness probe consumed by load balancers and uptime monitors.
  - name: Inventory
    description: |
      CRUD over individual inventory items. Items are created by the manufacturing pipeline
      (`POST /admin/inventory/item` is the entry point), consumed by the shipping pipeline
      (`PUT /admin/shipper/shipment/:id` with `action: PickInventoryItem`), and reported on
      via `/stats` and `/report`. The shipping pipeline mutates `status` automatically — most
      callers of this module do not need to write `status` directly.
  - name: Fusion
    description: |
      Toolpath generation handed to Fusion 360 workstations on the factory network.
      Authenticated by `fusionApiKey`; no per-user access token (LB-gated).
  - name: CNC
    description: |
      Toolpath delivery and state updates from CNC machines on the factory floor.
      Authenticated by `cncMachineApiKey` + a `CncMachine` access token for the operator.
  - name: Pose
    description: |
      Pose-analysis factory tool. Authenticated by `poseApiKey` + a `PoseAppUser` access token.
  - name: RDC
    description: |
      Bigscreen "RDC" build catalog (Rendering Data Container). Listing and selecting the
      build that runs in production.
  - name: Topics
    description: |
      Marketing topics — short editorial labels surfaced in the Bigscreen Unity app.
  - name: Workers
    description: |
      Live status of background worker threads spawned within `admin_api` (sync jobs,
      shipper queue refresh, KB ingest, CSV exports, etc.).
  - name: Knowledge Base
    description: |
      The internal knowledge base service — natural-language queries, search, document
      ingestion (Discord, GitHub, files), and feedback. Hosted by `apps/kb`.
  - name: Channels
    description: |
      Bigscreen TV — channels (collections of streams), channel groups, and stream entries.
  - name: Media
    description: |
      Media products, items, entitlements, categories, collections, and coupons surfaced
      to the Bigscreen Unity app.
  - name: Accounts
    description: |
      Bigscreen staff account creation, access-policy management, and the
      `GET /admin/access/*` probes used by Arda to gate UI.
  - name: Moderation
    description: |
      Account / room moderation: bans, reports, device history, IP allowlisting,
      notes, login tokens, and analytics for a target account.
  - name: OAuth
    description: |
      OAuth 2.0 admin: clients, grants, scopes, audit log, step-up authentication.
      Powers the OAuth provider that arda fronts.
  - name: Fabricator Scans
    description: |
      Customer face-scan ingestion and processing — scan requests, topology data,
      mesh files, and GDPR scan-data deletion.
  - name: Fabricator Jobs
    description: |
      Manufacturing job pipeline — creation from scan requests, the state machine,
      toolpaths, debug actions, and reports.
  - name: Fabricator Orders
    description: |
      Shopify order integration, order pairing (Beyond + lens inserts), Big Orders
      (Bigscreen's first-party order records mirrored to/from Shopify), bulk sync,
      reports, and Shopify webhook management.
  - name: Shipping
    description: |
      Shipment state machine (creation, picking, label purchase, dispatch), Shippo
      integration, and analytics. Sits at the end of the order pipeline.
  - name: DHL
    description: |
      DHL invoice + per-tracking-number charge ingestion, browsing, reconciliation,
      and flagging. Driven by manual MyBill CSV uploads.
paths:
  /health:
    get:
      tags:
        - Health
      operationId: getHealth
      summary: Liveness probe
      description: |
        Unauthenticated health probe. Returns the literal string `"OK"` as
        `text/plain`. Used by load balancers and uptime monitors.
      security: []
      responses:
        '200':
          description: Server is up.
          content:
            text/plain:
              schema:
                type: string
                example: OK
  /fusion/whoami:
    get:
      tags:
        - Fusion
      operationId: fusionWhoami
      summary: Fusion auth probe
      description: |
        Returns the fusion-api auth status — used by Fusion 360 workstations to confirm
        their `fusion-api` bearer is valid before pulling a toolpath job.
      security:
        - fusionApiKey: []
      responses:
        '200':
          description: Caller's fusion auth status.
          content:
            application/json:
              schema:
                type: object
                properties:
                  authorizedApp:
                    type: string
                    description: Application id authenticated for the request.
                  authenticated:
                    type: boolean
        '401':
          $ref: '#/components/responses/Unauthorized'
  /fusion/next:
    get:
      tags:
        - Fusion
      operationId: fusionNext
      summary: Pull the next toolpath generation job
      description: |
        Returns the next manufacturing job that is ready for Fusion-360-side toolpath
        generation, including a compressed copy of the scan to consume. The job is
        advanced to the relevant in-progress state as part of this call.
      security:
        - fusionApiKey: []
      responses:
        '200':
          description: Next job to process, with its compressed scan.
          content:
            application/json:
              schema:
                type: object
                properties:
                  jobId:
                    type: string
                  toolPathId:
                    type: string
                  ipd:
                    type: integer
                  meshData:
                    type: string
                    description: Deflate-compressed, base64-encoded mesh.
        '401':
          $ref: '#/components/responses/Unauthorized'
  /fusion/{toolPathId}:
    parameters:
      - in: path
        name: toolPathId
        required: true
        schema:
          type: string
        description: The toolpath id returned by `/fusion/next`.
    put:
      tags:
        - Fusion
      operationId: fusionUploadToolPath
      summary: Upload a completed toolpath file
      description: |
        Uploads the finished toolpath file produced by Fusion 360 for the given job.
        The request body is the raw file (`multipart/form-data`); the handler is wrapped
        in `toolPathUploadHandler`.
      security:
        - fusionApiKey: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                  description: The toolpath file produced by Fusion 360.
      responses:
        '200':
          description: Toolpath stored.
        '401':
          $ref: '#/components/responses/Unauthorized'
  /cnc:
    get:
      tags:
        - CNC
      operationId: cncHealth
      summary: CNC auth probe
      description: Health probe — returns the literal string `"OK"` if the caller's CNC credentials are valid.
      security:
        - cncMachineApiKey: []
          cncAccessToken: []
      responses:
        '200':
          description: Caller is authenticated.
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - CncMachine
        - Fabricator
  /cnc/next:
    put:
      tags:
        - CNC
      operationId: cncStartNext
      summary: Pull the next CNC manufacturing job
      description: |
        Atomically claims the next available toolpath job for this CNC operator and
        returns the toolpath payload to execute on the machine.
      security:
        - cncMachineApiKey: []
          cncAccessToken: []
      responses:
        '200':
          description: Next manufacturing job's toolpath payload.
          content:
            application/json:
              schema:
                type: object
                properties:
                  jobId:
                    type: string
                  toolPathId:
                    type: string
                  waxId:
                    type: string
                    description: 20-bit BitField stamped on the wax block.
                  ipd:
                    type: integer
                  gcode:
                    type: string
                    description: G-code body, filtered by the WaxId's bore bits.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - CncMachine
        - Fabricator
  /cnc/{toolPathId}:
    parameters:
      - in: path
        name: toolPathId
        required: true
        schema:
          type: string
        description: Toolpath id returned by `/cnc/next`.
    get:
      tags:
        - CNC
      operationId: cncGetToolPathState
      summary: Read the current state of a toolpath
      description: Returns the persisted state of a toolpath job — used by the CNC operator to verify the server's view before reporting completion.
      security:
        - cncMachineApiKey: []
          cncAccessToken: []
      responses:
        '200':
          description: Current toolpath state record.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  jobId:
                    type: string
                  state:
                    type: string
                    description: e.g. `pending`, `started`, `paused`, `completed`, `error`.
                  waxId:
                    type: string
                  operatorBigscreenAccountId:
                    type: string
                  startedAt:
                    type: string
                    format: date-time
                  completedAt:
                    type:
                      - string
                      - 'null'
                    format: date-time
                  lastUpdatedAt:
                    type: string
                    format: date-time
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - CncMachine
        - Fabricator
    put:
      tags:
        - CNC
      operationId: cncUpdateToolPathState
      summary: Update toolpath state from the CNC machine
      description: Reports state transitions from the CNC machine back to admin_api (started, paused, completed, error, etc.).
      security:
        - cncMachineApiKey: []
          cncAccessToken: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
              description: Free-form update payload — see handler at `FabricatorAdminApi.updateToolPathForCnc`.
      responses:
        '200':
          description: State updated.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - CncMachine
        - Fabricator
  /admin/rdc:
    get:
      tags:
        - RDC
      operationId: getRDCList
      summary: List RDC builds
      description: Returns the catalog of available RDC (Rendering Data Container) builds.
      responses:
        '200':
          description: Build list.
          content:
            application/json:
              schema:
                type: object
                properties:
                  builds:
                    type: array
                    items:
                      type: object
                      properties:
                        build:
                          type: string
                          description: Build identifier.
                        createdAt:
                          type: string
                          format: date-time
                        url:
                          type: string
                          format: uri
                        isActive:
                          type: boolean
                  count:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - admin:all
  /admin/rdc/active:
    get:
      tags:
        - RDC
      operationId: getActiveRDC
      summary: Currently active RDC build
      description: Returns the build that is currently flagged as the active production RDC.
      responses:
        '200':
          description: Active build identifier and metadata.
          content:
            application/json:
              schema:
                type: object
                properties:
                  build:
                    type: string
                  createdAt:
                    type: string
                    format: date-time
                  url:
                    type: string
                    format: uri
                  activatedAt:
                    type: string
                    format: date-time
                  activatedByBigscreenAccountId:
                    type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - admin:all
  /admin/rdc/active/{build}:
    parameters:
      - in: path
        name: build
        required: true
        schema:
          type: string
        description: Build identifier to mark active.
    put:
      tags:
        - RDC
      operationId: setActiveRDC
      summary: Set the active RDC build
      description: Promotes the supplied build identifier to be the active production RDC build.
      responses:
        '200':
          description: Active build updated.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - admin:all
  /admin/topic:
    post:
      tags:
        - Topics
      operationId: createTopic
      summary: Create a topic
      description: |
        Creates a marketing topic. The `name` field is auto-converted to a URL-friendly
        slug; any other topic fields supported by the underlying schema can be supplied.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - title
                - name
              properties:
                title:
                  type: string
                  description: Display title.
                name:
                  type: string
                  description: Human-supplied name (auto-slugified).
              additionalProperties: true
      responses:
        '200':
          description: Created topic.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Topic'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - events:write
        - admin:all
  /admin/topic/{id}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
        description: Topic id.
    put:
      tags:
        - Topics
      operationId: updateTopic
      summary: Update a topic
      description: Updates an existing topic by id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        '200':
          description: Updated topic.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Topic'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Topic not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - events:write
        - admin:all
    delete:
      tags:
        - Topics
      operationId: deleteTopic
      summary: Delete a topic
      description: Deletes a topic by id.
      responses:
        '200':
          description: Topic deleted.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Topic not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - events:write
        - admin:all
  /admin/topics:
    get:
      tags:
        - Topics
      operationId: getTopics
      summary: List all topics
      description: Returns every topic.
      responses:
        '200':
          description: Array of topic records.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Topic'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - events:read
        - admin:all
  /admin/workers/status:
    get:
      tags:
        - Workers
      operationId: getWorkersStatus
      summary: Live worker thread status
      description: |
        Returns the list of currently-active and recently-completed background workers
        (Big Order sync, shipper queue refresh, Shippo analytics, KB ingest, CSV exports,
        etc.) tracked by `setupWorkerTracking()`. Recently-completed workers are
        auto-pruned from the response after 60 seconds.
      responses:
        '200':
          description: Worker tracking snapshot.
          content:
            application/json:
              schema:
                type: object
                required:
                  - workers
                properties:
                  workers:
                    type: array
                    items:
                      type: object
                      required:
                        - name
                        - startedBy
                        - startedAt
                        - durationMs
                        - status
                      properties:
                        name:
                          type: string
                          description: Worker task name (e.g. `SyncBigOrders`, `RefreshShipperQueue`).
                        startedBy:
                          type: string
                          description: Requesting account id / username.
                        startedAt:
                          type: string
                          format: date-time
                        durationMs:
                          type: integer
                          description: Live for active workers; frozen at completion for finished ones.
                        status:
                          type: string
                          enum:
                            - running
                            - completed
                            - failed
                        completedAt:
                          type:
                            - string
                            - 'null'
                          format: date-time
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Fabricator
        - FabricatorReadOnly
        - Admin
      x-scopes:
        - admin:all
  /pose/whoami:
    get:
      tags:
        - Pose
      operationId: poseWhoami
      summary: Pose-app auth probe
      description: Returns the pose-api auth status — used by the pose-analysis factory tool to confirm credentials before pulling jobs.
      security:
        - poseApiKey: []
          poseAccessToken: []
      responses:
        '200':
          description: Caller's pose auth status.
          content:
            application/json:
              schema:
                type: object
                properties:
                  authorizedApp:
                    type: string
                  accountId:
                    type: string
                  username:
                    type: string
                  authenticated:
                    type: boolean
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - PoseAppUser
  /pose/schemas:
    get:
      tags:
        - Pose
      operationId: poseGetSchemas
      summary: Fabricator schema enumerations
      description: Returns the same schema enumerations as `/admin/fabricator/schemas2` — exposed under `/pose/*` so the pose tool can hit it without admin credentials.
      security:
        - poseApiKey: []
          poseAccessToken: []
      responses:
        '200':
          description: V2 schema enumerations.
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: array
                  items:
                    type: string
                description: Each top-level key is an enum name; the value is the list of legal members.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          description: Rate-limited.
      x-access-policies:
        - PoseAppUser
  /pose/jobs:
    get:
      tags:
        - Pose
      operationId: poseGetJobs
      summary: List jobs (filtered)
      description: Returns manufacturing jobs with filtering — a pose-tool-friendly subset of `/admin/fabricator/jobs`.
      security:
        - poseApiKey: []
          poseAccessToken: []
      responses:
        '200':
          description: Jobs page.
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/Job'
                  count:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          description: Rate-limited.
      x-access-policies:
        - PoseAppUser
  /pose/job/{jobId}:
    parameters:
      - in: path
        name: jobId
        required: true
        schema:
          type: string
        description: Manufacturing job id.
    get:
      tags:
        - Pose
      operationId: poseGetJob
      summary: Fetch one job by id
      description: Returns a single manufacturing job by id.
      security:
        - poseApiKey: []
          poseAccessToken: []
      responses:
        '200':
          description: The job record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Job'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No job with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate-limited.
      x-access-policies:
        - PoseAppUser
    put:
      tags:
        - Pose
      operationId: poseUpdateJob
      summary: Update a job
      description: |
        Updates fields on a manufacturing job — pose-app-friendly variant of
        `PUT /admin/fabricator/job/{jobId}`. Subject to the same state-machine rules.
      security:
        - poseApiKey: []
          poseAccessToken: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        '200':
          description: Updated job record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Job'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No job with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate-limited.
      x-access-policies:
        - PoseAppUser
  /pose/job/{jobId}/scan:
    parameters:
      - in: path
        name: jobId
        required: true
        schema:
          type: string
    get:
      tags:
        - Pose
      operationId: poseDownloadScan
      summary: Download the scan zip
      description: |
        Downloads the raw scan zip uploaded by the customer's iOS face-scan app.
        Subject to a separate, stricter rate limiter than the rest of `/pose/*` —
        scan files are large.
      security:
        - poseApiKey: []
          poseAccessToken: []
      responses:
        '200':
          description: Scan zip.
          content:
            application/zip:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No job or no scan file for this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate-limited (separate limiter from the rest of `/pose/*`).
      x-access-policies:
        - PoseAppUser
  /pose/job/{jobId}/mesh:
    parameters:
      - in: path
        name: jobId
        required: true
        schema:
          type: string
    get:
      tags:
        - Pose
      operationId: poseDownloadMesh
      summary: Download the processed mesh
      description: Downloads the processed mesh file (output of the scan preprocessor) for a job.
      security:
        - poseApiKey: []
          poseAccessToken: []
      responses:
        '200':
          description: Mesh file.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No mesh file for this job.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate-limited.
      x-access-policies:
        - PoseAppUser
  /pose/job/{jobId}/debug_start_toolpath:
    parameters:
      - in: path
        name: jobId
        required: true
        schema:
          type: string
    get:
      tags:
        - Pose
      operationId: poseDebugStartToolPath
      summary: Debug — start toolpath generation
      description: Debug helper that kicks off toolpath generation for a job from the pose tool.
      security:
        - poseApiKey: []
          poseAccessToken: []
      responses:
        '200':
          description: Toolpath generation started.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          description: Rate-limited.
      x-access-policies:
        - PoseAppUser
  /pose/job/{jobId}/debug_upload_toolpath:
    parameters:
      - in: path
        name: jobId
        required: true
        schema:
          type: string
    put:
      tags:
        - Pose
      operationId: poseDebugUploadToolPath
      summary: Debug — upload a toolpath
      description: Debug helper to manually upload a generated toolpath file for a job.
      security:
        - poseApiKey: []
          poseAccessToken: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
      responses:
        '200':
          description: Toolpath stored.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          description: Rate-limited.
      x-access-policies:
        - PoseAppUser
  /pose/job/{jobId}/toolpath:
    parameters:
      - in: path
        name: jobId
        required: true
        schema:
          type: string
    get:
      tags:
        - Pose
      operationId: poseDownloadToolPath
      summary: Download the G-code toolpath
      description: Downloads the G-code toolpath file produced for the job, filtered by the job's WaxId.
      security:
        - poseApiKey: []
          poseAccessToken: []
      responses:
        '200':
          description: Toolpath file.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No toolpath for this job.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate-limited.
      x-access-policies:
        - PoseAppUser
  /admin/fabricator/schemas:
    get:
      tags:
        - Fabricator Scans
      operationId: getFabricatorSchemas
      summary: Fabricator schema enumerations (v1)
      description: Returns enumerations consumed by the manufacturing pipeline (job states, scan-request states, etc.).
      responses:
        '200':
          description: Schema enumeration payload.
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: array
                  items:
                    type: string
                description: Each top-level key is an enum name; the value is the list of legal members.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Inventory
        - CncMachine
        - Fabricator
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/schemas2:
    get:
      tags:
        - Fabricator Scans
      operationId: getFabricatorSchemas2
      summary: Fabricator schema enumerations (v2)
      description: |
        V2 of `/admin/fabricator/schemas` — also includes BigOrder-related schemas. Prefer
        this over v1 for new integrations.
      responses:
        '200':
          description: V2 schema enumeration payload.
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: array
                  items:
                    type: string
                description: Each top-level key is an enum name; the value is the list of legal members.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Inventory
        - CncMachine
        - Fabricator
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/scan_requests:
    get:
      tags:
        - Fabricator Scans
      operationId: getScanRequests
      summary: List all scan requests
      description: Returns every scan request known to the system.
      responses:
        '200':
          description: Array of scan requests.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ScanRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/scan_requests/repair:
    get:
      tags:
        - Fabricator Scans
      operationId: repairScanRequests
      summary: Repair scan requests
      description: Walks the scan-request table and repairs any rows that have drifted out of a consistent state. Idempotent.
      responses:
        '200':
          description: Repair summary.
          content:
            application/json:
              schema:
                type: object
                properties:
                  scanRequestsExamined:
                    type: integer
                  scanRequestsRepaired:
                    type: integer
                  durationMs:
                    type: integer
                  errors:
                    type: array
                    items:
                      type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/account/{bigscreenAccountId}/scan_requests:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Scans
      operationId: getScanRequestsForAccount
      summary: List scan requests for an account
      description: Returns scan requests submitted by the supplied Bigscreen account.
      responses:
        '200':
          description: Array of scan requests.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ScanRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Fabricator
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/account/{bigscreenAccountId}/scan_data:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    delete:
      tags:
        - Fabricator Scans
      operationId: deleteScanDataGDPR
      summary: GDPR — delete scan data for an account
      description: |
        Deletes (or, in dry-run mode, reports on) all scan data the system holds for the
        supplied Bigscreen account. Used to satisfy GDPR right-to-erasure requests.
        Returns the inventory of what was found / deleted.
      parameters:
        - in: query
          name: dryRun
          required: false
          schema:
            type: string
            enum:
              - '1'
          description: If set, returns the data inventory without deleting anything.
      responses:
        '200':
          description: Deletion (or dry-run inventory) result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  bigscreenAccountId:
                    type: string
                  dryRun:
                    type: boolean
                  scanRequestsFound:
                    type: integer
                  scanRequestsDeleted:
                    type: integer
                  jobsFound:
                    type: integer
                  jobsDeleted:
                    type: integer
                  scanFilesDeleted:
                    type: integer
                  meshFilesDeleted:
                    type: integer
                  durationMs:
                    type: integer
                  errors:
                    type: array
                    items:
                      type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          description: Missing or invalid `bigscreenAccountId`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - fabricator:write
        - admin:all
  /admin/fabricator/scan_request/{scanRequestId}:
    parameters:
      - in: path
        name: scanRequestId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Scans
      operationId: getScanRequest
      summary: Fetch a scan request
      description: Returns a single scan request by id.
      responses:
        '200':
          description: Scan request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScanRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No scan request with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - fabricator:read
        - admin:all
    put:
      tags:
        - Fabricator Scans
      operationId: updateScanRequest
      summary: Update a scan request
      description: Updates a scan request (state transitions, IPD overrides, etc.).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        '200':
          description: Updated scan request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScanRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No scan request with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - fabricator:write
        - admin:all
    delete:
      tags:
        - Fabricator Scans
      operationId: deleteScanRequest
      summary: Delete a scan request
      description: Hard-deletes a scan request. Restricted to `Admin`.
      responses:
        '200':
          description: Scan request deleted.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No scan request with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - fabricator:write
        - admin:all
  /admin/fabricator/scan_request/{scanRequestId}/topology/{topologyPatientIdOverride}:
    parameters:
      - in: path
        name: scanRequestId
        required: true
        schema:
          type: string
      - in: path
        name: topologyPatientIdOverride
        required: true
        schema:
          type: string
        description: |
          Patient-id override forwarded to Topology. The trailing override segment is
          technically optional in the registered route (`/topology{/:topologyPatientIdOverride}`);
          when omitted, the original patient id is used.
    get:
      tags:
        - Fabricator Scans
      operationId: updateScanRequestWithTopologyScan
      summary: Update a scan request with Topology data
      description: |
        Updates the scan request with data pulled from the Topology service. The
        `topologyPatientIdOverride` segment is optional in the underlying route.
      responses:
        '200':
          description: Updated scan request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScanRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No scan request with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - FabricatorAdmin
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/scan_request/{scanRequestId}/scan:
    parameters:
      - in: path
        name: scanRequestId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Scans
      operationId: getScanRequestScanFile
      summary: Download the scan file
      description: Downloads the raw scan file uploaded for this scan request.
      responses:
        '200':
          description: Scan file.
          content:
            application/zip:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No scan file for this request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - FabricatorAdmin
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/scan_request/{scanRequestId}/verify:
    parameters:
      - in: path
        name: scanRequestId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Scans
      operationId: verifyScanRequest
      summary: Verify a scan request
      description: Runs verification checks on a scan request and reports the result.
      responses:
        '200':
          description: Verification result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  scanRequestId:
                    type: string
                  verified:
                    type: boolean
                  ipd:
                    type: integer
                  preprocessorResult:
                    type: string
                  issues:
                    type: array
                    items:
                      type: object
                      properties:
                        severity:
                          type: string
                        code:
                          type: string
                        message:
                          type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No scan request with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/scan_request/{scanRequestId}/mesh:
    parameters:
      - in: path
        name: scanRequestId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Scans
      operationId: getScanRequestMesh
      summary: Download the mesh file
      description: Downloads the processed mesh file for the scan request.
      responses:
        '200':
          description: Mesh file.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No mesh file for this request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - FabricatorAdmin
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/kb/query:
    post:
      tags:
        - Knowledge Base
      operationId: kbQuery
      summary: Run a natural-language query
      description: Runs a natural-language query against the knowledge base and returns the model's answer plus citations.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - query
              properties:
                query:
                  type: string
                  description: Natural-language question.
                options:
                  type: object
                  properties:
                    limit:
                      type: integer
                      default: 10
                    sourceTypes:
                      type: array
                      items:
                        type: string
                      description: Restrict retrieval to these source types.
                    includeContext:
                      type: boolean
                      description: When true, sources include a short `excerpt`.
      responses:
        '200':
          description: Query result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KBQueryResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - KBUser
        - Admin
      x-scopes:
        - kb:write
        - admin:all
  /admin/kb/search:
    post:
      tags:
        - Knowledge Base
      operationId: kbSearch
      summary: Search the knowledge base
      description: Returns documents matching the supplied search query — vector + keyword search with the LLM step skipped.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - query
              properties:
                query:
                  type: string
                options:
                  type: object
                  properties:
                    limit:
                      type: integer
                      default: 10
                    sourceTypes:
                      type: array
                      items:
                        type: string
                    useReranking:
                      type: boolean
                      default: true
                      description: When false, skip the Cohere re-rank step.
      responses:
        '200':
          description: Search results.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KBSearchResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - KBUser
        - Admin
      x-scopes:
        - kb:write
        - admin:all
  /admin/kb/ingest/document:
    post:
      tags:
        - Knowledge Base
      operationId: kbIngestDocument
      summary: Ingest a document
      description: Adds a document to the knowledge base.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - sourceType
                - sourceId
                - title
                - content
              properties:
                sourceType:
                  type: string
                  description: Source class (e.g. `document`, `discord`, `github`).
                sourceId:
                  type: string
                  description: Source-specific identifier.
                sourceUrl:
                  type: string
                title:
                  type: string
                content:
                  type: string
                  description: Full document body to chunk and embed.
                contentType:
                  type: string
                  example: text/markdown
                metadata:
                  type: object
                  additionalProperties: true
                visibility:
                  type: string
                  description: e.g. `public`, `internal`.
                allowedGroups:
                  type: array
                  items:
                    type: string
      responses:
        '200':
          description: Ingestion result.
          content:
            application/json:
              schema:
                type: object
                required:
                  - success
                properties:
                  success:
                    type: boolean
                  documentId:
                    type: string
                  chunkCount:
                    type: integer
                    description: Number of chunks the document was split into.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - KBAdmin
        - Admin
      x-scopes:
        - kb:write
        - admin:all
  /admin/kb/ingest/discord:
    post:
      tags:
        - Knowledge Base
      operationId: kbIngestDiscord
      summary: Ingest Discord messages
      description: |
        Pulls and ingests messages from configured Discord channels. **Note:**
        currently disabled (returns `501 Not Implemented`) pending a discord.js
        compatibility fix.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                channels:
                  type: array
                  items:
                    type: string
                  description: When set, restrict ingestion to these channel ids.
      responses:
        '200':
          description: Ingestion result (when re-enabled).
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  channelsSynced:
                    type: integer
                  messagesSynced:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '501':
          description: Discord integration is temporarily disabled.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - KBAdmin
        - Admin
      x-scopes:
        - kb:write
        - admin:all
  /admin/kb/ingest/github:
    post:
      tags:
        - Knowledge Base
      operationId: kbIngestGitHub
      summary: Ingest GitHub content
      description: Ingests issues, PRs, commits, README/wiki content from configured GitHub repositories.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                repos:
                  type: array
                  description: When set, override the env-configured repo list.
                  items:
                    type: object
                    properties:
                      owner:
                        type: string
                      repo:
                        type: string
                      includeIssues:
                        type: boolean
                      includePullRequests:
                        type: boolean
                      includeCommits:
                        type: boolean
                      includeWiki:
                        type: boolean
                    additionalProperties: true
      responses:
        '200':
          description: Ingestion result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KBIngestionResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - KBAdmin
        - Admin
      x-scopes:
        - kb:write
        - admin:all
  /admin/kb/ingest/github/cleanup-commits:
    post:
      tags:
        - Knowledge Base
      operationId: kbCleanupGitHubCommits
      summary: Clean up ingested GitHub commits
      description: Deletes previously-ingested GitHub commit documents that no longer match the configured ingestion filters.
      responses:
        '200':
          description: Cleanup result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KBIngestionResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - KBAdmin
        - Admin
      x-scopes:
        - kb:write
        - admin:all
  /admin/kb/sync:
    get:
      tags:
        - Knowledge Base
      operationId: kbSync
      summary: Sync the knowledge base
      description: Triggers a sync that reconciles the KB with its upstream sources.
      responses:
        '200':
          description: Sync result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KBIngestionResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - FabricatorReadOnly
        - Admin
      x-scopes:
        - kb:read
        - admin:all
  /admin/kb/document/{sourceType}/{sourceId}:
    parameters:
      - in: path
        name: sourceType
        required: true
        schema:
          type: string
        description: Source type identifier (e.g. `discord`, `github`, `document`).
      - in: path
        name: sourceId
        required: true
        schema:
          type: string
        description: Source-specific identifier for the document.
    delete:
      tags:
        - Knowledge Base
      operationId: kbDeleteDocumentBySource
      summary: Delete a document by source
      description: Removes the document identified by the supplied source type + source id from the KB.
      responses:
        '200':
          description: Delete result.
          content:
            application/json:
              schema:
                type: object
                required:
                  - success
                properties:
                  success:
                    type: boolean
                  documentsDeleted:
                    type: integer
                  chunksDeleted:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No document for the supplied source.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - KBAdmin
        - Admin
      x-scopes:
        - kb:write
        - admin:all
  /admin/kb/documents:
    get:
      tags:
        - Knowledge Base
      operationId: kbGetDocuments
      summary: List KB documents
      description: Returns documents currently in the knowledge base, with optional source-type filter and pagination.
      parameters:
        - in: query
          name: sourceType
          schema:
            type: string
          description: Optional filter — restrict to one source type.
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Documents listing.
          content:
            application/json:
              schema:
                type: object
                required:
                  - documents
                  - pagination
                properties:
                  documents:
                    type: array
                    items:
                      $ref: '#/components/schemas/KBDocument'
                  pagination:
                    type: object
                    properties:
                      limit:
                        type: integer
                      offset:
                        type: integer
                      total:
                        type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - KBAdmin
        - Admin
      x-scopes:
        - kb:read
        - admin:all
  /admin/kb/document/{documentId}:
    parameters:
      - in: path
        name: documentId
        required: true
        schema:
          type: string
    get:
      tags:
        - Knowledge Base
      operationId: kbGetDocument
      summary: Fetch one KB document
      description: Returns a specific document by id, plus its individual chunk metadata.
      responses:
        '200':
          description: Document record with embedded chunk index.
          content:
            application/json:
              schema:
                type: object
                required:
                  - document
                  - chunks
                properties:
                  document:
                    $ref: '#/components/schemas/KBDocument'
                  chunks:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        chunk_index:
                          type: integer
                        content_tokens:
                          type: integer
                        start_char:
                          type: integer
                        end_char:
                          type: integer
                        created_at:
                          type: string
                          format: date-time
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No document with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - KBAdmin
        - Admin
      x-scopes:
        - kb:read
        - admin:all
  /admin/kb/clear:
    post:
      tags:
        - Knowledge Base
      operationId: kbClear
      summary: Clear all KB documents
      description: |
        Hard-deletes every document in the knowledge base. Optional `sourceType`
        in the body filters the deletion. **Destructive** — `confirm` must be the
        literal string `DELETE_ALL_DOCUMENTS`. Restricted to `Admin`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - confirm
              properties:
                confirm:
                  type: string
                  enum:
                    - DELETE_ALL_DOCUMENTS
                  description: Must be the literal string `DELETE_ALL_DOCUMENTS`.
                sourceType:
                  type: string
                  description: When set, restrict deletion to one source type.
      responses:
        '200':
          description: KB cleared.
          content:
            application/json:
              schema:
                type: object
                required:
                  - success
                  - documentsDeleted
                  - chunksDeleted
                  - sourceType
                properties:
                  success:
                    type: boolean
                  documentsDeleted:
                    type: integer
                  chunksDeleted:
                    type: integer
                  sourceType:
                    type: string
                    description: '`"all"` or the supplied filter.'
        '400':
          description: Confirmation missing or wrong.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - kb:write
        - admin:all
  /admin/kb/stats:
    get:
      tags:
        - Knowledge Base
      operationId: kbStats
      summary: KB statistics
      description: Returns aggregate statistics about the knowledge base (document counts, ingestion timestamps, etc.).
      responses:
        '200':
          description: Stats payload.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KBStats'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - KBAdmin
        - Admin
      x-scopes:
        - kb:read
        - admin:all
  /admin/kb/logs:
    get:
      tags:
        - Knowledge Base
      operationId: kbLogs
      summary: Query logs
      description: Returns query log entries — what users have asked, when, and how it was answered.
      parameters:
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Query log entries.
          content:
            application/json:
              schema:
                type: object
                required:
                  - logs
                  - pagination
                properties:
                  logs:
                    type: array
                    items:
                      $ref: '#/components/schemas/KBQueryLog'
                  pagination:
                    type: object
                    properties:
                      limit:
                        type: integer
                      offset:
                        type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - KBAdmin
        - Admin
      x-scopes:
        - kb:read
        - admin:all
  /admin/kb/feedback/{queryLogId}:
    parameters:
      - in: path
        name: queryLogId
        required: true
        schema:
          type: string
        description: Query log id to attach feedback to.
    post:
      tags:
        - Knowledge Base
      operationId: kbSubmitFeedback
      summary: Submit feedback on a query
      description: Submits user feedback (thumbs-up/down + comments) for a given query log entry.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                score:
                  type: integer
                  description: '`1` = thumbs-up, `-1` = thumbs-down.'
                text:
                  type: string
                  description: Optional free-text follow-up.
      responses:
        '200':
          description: Feedback recorded.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - KBUser
        - Admin
      x-scopes:
        - kb:write
        - admin:all
  /admin/tv/channel:
    post:
      tags:
        - Channels
      operationId: createChannel
      summary: Create a TV channel
      description: Creates a TV channel.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - title
                - description
              properties:
                name:
                  type: string
                title:
                  type: string
                description:
                  type: string
      responses:
        '200':
          description: Created channel.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Channel'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
    put:
      tags:
        - Channels
      operationId: updateChannel
      summary: Update a TV channel
      description: Updates a TV channel. The id is supplied in the body.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
              properties:
                id:
                  type: string
              additionalProperties: true
      responses:
        '200':
          description: Updated channel.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Channel'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/tv/channel/{channelId}:
    parameters:
      - in: path
        name: channelId
        required: true
        schema:
          type: string
    delete:
      tags:
        - Channels
      operationId: deleteChannel
      summary: Delete a channel
      description: Deletes a TV channel by id.
      responses:
        '200':
          description: Channel deleted.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No channel with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
    get:
      tags:
        - Channels
      operationId: getChannel
      summary: Fetch a channel
      description: Returns a channel by id.
      responses:
        '200':
          description: Channel record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Channel'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No channel with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/tv/Channels:
    get:
      tags:
        - Channels
      operationId: getChannels
      summary: List all channels
      description: Returns every TV channel.
      responses:
        '200':
          description: Array of channels.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Channel'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/tv/stream:
    post:
      tags:
        - Channels
      operationId: addStreamToChannel
      summary: Add a stream to a channel
      description: Adds a stream entry to an existing channel.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - channelId
                - stream
              properties:
                channelId:
                  type: string
                stream:
                  type: object
                  description: '`StreamData` payload (see `LegacySchemas.StreamData`).'
                  properties:
                    uri:
                      type: string
                      format: uri
                    isAvailable:
                      type: boolean
                    type:
                      type: string
                  additionalProperties: true
      responses:
        '200':
          description: Updated channel.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Channel'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/tv/stream/{channelId}/{streamIndex}:
    parameters:
      - in: path
        name: channelId
        required: true
        schema:
          type: string
      - in: path
        name: streamIndex
        required: true
        schema:
          type: integer
        description: Zero-based index of the stream within the channel's stream array.
    delete:
      tags:
        - Channels
      operationId: deleteStreamFromChannel
      summary: Remove a stream from a channel
      description: Removes the stream at the given index from a channel.
      responses:
        '200':
          description: Stream removed.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No channel or no stream at that index.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/tv/channel_query/{name}:
    parameters:
      - in: path
        name: name
        required: true
        schema:
          type: string
        description: Channel-query name.
    get:
      tags:
        - Channels
      operationId: getChannelQueryValues
      summary: Channel query values
      description: |
        Returns channel query values by name. The query name is one of a small set of
        named queries baked into `api/src/Channels.ts`; values are the live query
        results expanded into stream entries.
      responses:
        '200':
          description: Query values.
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                  streams:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  count:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/tv/channel_group:
    post:
      tags:
        - Channels
      operationId: createChannelGroup
      summary: Create a channel group
      description: Creates a channel group — a collection of channels organized for the Bigscreen TV UI.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - title
              properties:
                name:
                  type: string
                  description: URL slug.
                title:
                  type: string
                channelIds:
                  type: array
                  items:
                    type: string
                isPublished:
                  type: boolean
                  default: true
                imageUrl:
                  type: string
                  format: uri
      responses:
        '200':
          description: Created channel group.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ChannelGroup'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
    put:
      tags:
        - Channels
      operationId: updateChannelGroup
      summary: Update a channel group
      description: Updates a channel group. The id is supplied in the body.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
              properties:
                id:
                  type: string
              additionalProperties: true
      responses:
        '200':
          description: Updated channel group.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ChannelGroup'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/tv/channel_group/{channelGroupId}:
    parameters:
      - in: path
        name: channelGroupId
        required: true
        schema:
          type: string
    get:
      tags:
        - Channels
      operationId: getChannelGroup
      summary: Fetch a channel group
      description: Returns a channel group by id.
      responses:
        '200':
          description: Channel group record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ChannelGroup'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No channel group with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
    delete:
      tags:
        - Channels
      operationId: deleteChannelGroup
      summary: Delete a channel group
      description: Deletes a channel group by id.
      responses:
        '200':
          description: Channel group deleted.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No channel group with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/tv/rebuild_channel_group/{channelGroupId}:
    parameters:
      - in: path
        name: channelGroupId
        required: true
        schema:
          type: string
    get:
      tags:
        - Channels
      operationId: rebuildChannelGroup
      summary: Rebuild / refresh a channel group
      description: Rebuilds the channel group's underlying texture/cached state.
      responses:
        '200':
          description: Rebuild summary.
          content:
            application/json:
              schema:
                type: object
                properties:
                  channelGroupId:
                    type: string
                  texturesRebuilt:
                    type: integer
                  channelsProcessed:
                    type: integer
                  durationMs:
                    type: integer
                  errors:
                    type: array
                    items:
                      type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/tv/channel_groups:
    get:
      tags:
        - Channels
      operationId: getChannelGroups
      summary: List all channel groups
      description: Returns every channel group.
      responses:
        '200':
          description: Array of channel groups.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ChannelGroup'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/oauth/lookup_client:
    get:
      tags:
        - OAuth
      operationId: oauthLookupClient
      summary: Look up an OAuth client by public client_id
      description: |
        Returns a client by its public `client_id`. Used by arda's `/oauth/authorize`
        consent UI to render the application name, logo, and requested scopes before
        the user approves access.
      responses:
        '200':
          description: Public client info.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthClient'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No client with the supplied `client_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - BigscreenDefault
  /admin/oauth/my_grant:
    get:
      tags:
        - OAuth
      operationId: oauthGetMyGrant
      summary: Get the caller's existing grant for a client
      description: Returns the grant (set of approved scopes) the calling user has previously given to a specified client. Empty when none exists.
      responses:
        '200':
          description: Grant record (or empty).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthGrant'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - BigscreenDefault
    post:
      tags:
        - OAuth
      operationId: oauthUpsertMyGrant
      summary: Create or update the caller's grant
      description: Records the scopes the caller has just approved during the consent flow. Idempotent.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - clientId
                - scopes
              properties:
                clientId:
                  type: string
                  description: OAuth client the user is approving.
                scopes:
                  type: array
                  items:
                    type: string
                  description: Scopes the user agreed to.
      responses:
        '200':
          description: Grant stored.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthGrant'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - BigscreenDefault
  /admin/oauth/codes:
    post:
      tags:
        - OAuth
      operationId: oauthMintCode
      summary: Mint an authorization code (arda only)
      description: |
        Mints an OAuth authorization code that arda redirects the caller back to the
        requesting client with. Only arda is expected to call this endpoint, after the
        user has consented.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - clientId
                - redirectUri
                - scopes
              properties:
                clientId:
                  type: string
                redirectUri:
                  type: string
                  format: uri
                scopes:
                  type: array
                  items:
                    type: string
                state:
                  type: string
                  description: Opaque value the client passed in /authorize, replayed on the callback.
                codeChallenge:
                  type: string
                  description: PKCE code challenge.
                codeChallengeMethod:
                  type: string
                  enum:
                    - S256
                    - plain
      responses:
        '200':
          description: Authorization code.
          content:
            application/json:
              schema:
                type: object
                required:
                  - code
                properties:
                  code:
                    type: string
                    description: Short-lived code the client redeems at the OAuth `/token` endpoint.
                  expiresIn:
                    type: integer
                    description: Seconds until the code expires.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - BigscreenDefault
  /admin/oauth/audit_event:
    post:
      tags:
        - OAuth
      operationId: oauthWriteAuditEvent
      summary: Write a consent-flow audit event (arda only)
      description: Appends an event to the OAuth audit log from arda during the consent flow (page views, denies, etc.).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - eventType
              properties:
                eventType:
                  type: string
                  description: Audit event class — e.g. `consent_viewed`, `consent_approved`, `consent_denied`.
                clientId:
                  type: string
                details:
                  type: object
                  additionalProperties: true
      responses:
        '200':
          description: Event recorded.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - BigscreenDefault
  /admin/oauth/clients/scopes:
    get:
      tags:
        - OAuth
      operationId: oauthGetScopes
      summary: List available scopes
      description: Returns the catalog of OAuth scopes that clients can request.
      responses:
        '200':
          description: Array of scope definitions.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  required:
                    - name
                  properties:
                    name:
                      type: string
                      description: Scope identifier (e.g. `openid`, `profile`, `admin:all`).
                    description:
                      type: string
                    requiresAdminApproval:
                      type: boolean
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Moderator
        - Fabricator
        - SuperUser
  /admin/oauth/clients:
    get:
      tags:
        - OAuth
      operationId: oauthListClients
      summary: List OAuth clients visible to the caller
      description: |
        Returns OAuth clients the caller can see. SuperUser sees every client; other
        staff see all clients but with read-write only on those they own.
      responses:
        '200':
          description: Array of client records.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/OAuthClient'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Moderator
        - Fabricator
        - SuperUser
    post:
      tags:
        - OAuth
      operationId: oauthCreateClient
      summary: Create an OAuth client
      description: Creates a new OAuth client. The caller becomes the owner.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - redirectUris
                - allowedScopes
                - serverIps
              properties:
                name:
                  type: string
                description:
                  type: string
                logoUrl:
                  type: string
                  format: uri
                homepageUrl:
                  type: string
                  format: uri
                redirectUris:
                  type: array
                  items:
                    type: string
                    format: uri
                allowedScopes:
                  type: array
                  items:
                    type: string
                serverIps:
                  type: array
                  items:
                    type: string
                  description: IP allowlist (required — clients must call from one of these IPs).
                clientType:
                  type: string
                  enum:
                    - confidential
                  description: Only `confidential` is supported in v1.
      responses:
        '200':
          description: Created client (with the secret returned exactly once as `plaintextSecret`).
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/OAuthClient'
                  - type: object
                    required:
                      - plaintextSecret
                    properties:
                      plaintextSecret:
                        type: string
                        description: Returned ONCE — capture it now.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Moderator
        - Fabricator
        - SuperUser
  /admin/oauth/clients/{uniqueId}:
    parameters:
      - in: path
        name: uniqueId
        required: true
        schema:
          type: string
    get:
      tags:
        - OAuth
      operationId: oauthGetClient
      summary: Fetch one OAuth client
      description: Returns a single OAuth client by its `uniqueId`.
      responses:
        '200':
          description: Client record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthClient'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No client with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - Moderator
        - Fabricator
        - SuperUser
    put:
      tags:
        - OAuth
      operationId: oauthUpdateClient
      summary: Update an OAuth client
      description: |
        Updates an OAuth client. Requires ownership-or-SuperUser **and** a recent
        step-up unlock (see `POST /step-up/challenge` and `/step-up/verify`).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                logoUrl:
                  type: string
                  format: uri
                homepageUrl:
                  type: string
                  format: uri
                redirectUris:
                  type: array
                  items:
                    type: string
                    format: uri
                allowedScopes:
                  type: array
                  items:
                    type: string
                serverIps:
                  type: array
                  items:
                    type: string
      responses:
        '200':
          description: Updated client.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthClient'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Moderator
        - Fabricator
        - SuperUser
    delete:
      tags:
        - OAuth
      operationId: oauthDeleteClient
      summary: Soft-delete an OAuth client
      description: |
        Soft-deletes an OAuth client (sets a deletion flag; row remains). Requires
        ownership-or-SuperUser + step-up unlock.
      responses:
        '200':
          description: Client soft-deleted.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Moderator
        - Fabricator
        - SuperUser
  /admin/oauth/clients/{uniqueId}/rotate_secret:
    parameters:
      - in: path
        name: uniqueId
        required: true
        schema:
          type: string
    post:
      tags:
        - OAuth
      operationId: oauthRotateSecret
      summary: Rotate the client secret
      description: |
        Rotates the client secret. The new secret is returned once in the response —
        callers must capture it. Requires ownership + step-up unlock.
      responses:
        '200':
          description: Rotation complete; new secret returned exactly once as `plaintextSecret`.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/OAuthClient'
                  - type: object
                    required:
                      - plaintextSecret
                    properties:
                      plaintextSecret:
                        type: string
                        description: Returned ONCE — capture it now.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Moderator
        - Fabricator
        - SuperUser
  /admin/oauth/clients/{uniqueId}/disable:
    parameters:
      - in: path
        name: uniqueId
        required: true
        schema:
          type: string
    post:
      tags:
        - OAuth
      operationId: oauthDisableClient
      summary: Disable an OAuth client
      description: Blocks further token issuance for the client without deleting the row.
      responses:
        '200':
          description: Client disabled.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Moderator
        - Fabricator
        - SuperUser
  /admin/oauth/clients/{uniqueId}/enable:
    parameters:
      - in: path
        name: uniqueId
        required: true
        schema:
          type: string
    post:
      tags:
        - OAuth
      operationId: oauthEnableClient
      summary: Re-enable a disabled client
      description: Reverses `/disable`, allowing the client to issue tokens again.
      responses:
        '200':
          description: Client enabled.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Moderator
        - Fabricator
        - SuperUser
  /admin/oauth/clients/{uniqueId}/grants:
    parameters:
      - in: path
        name: uniqueId
        required: true
        schema:
          type: string
    get:
      tags:
        - OAuth
      operationId: oauthListClientGrants
      summary: List grants for a client
      description: Lists user → client grant records for a specific OAuth client (each row is a Bigscreen account that has approved this client).
      responses:
        '200':
          description: Array of grants.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/OAuthGrant'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Moderator
        - Fabricator
        - SuperUser
  /admin/oauth/clients/{uniqueId}/audit:
    parameters:
      - in: path
        name: uniqueId
        required: true
        schema:
          type: string
    get:
      tags:
        - OAuth
      operationId: oauthClientAudit
      summary: Audit log for a specific client
      description: Returns audit-log entries scoped to a single OAuth client (creates, edits, secret rotations, disables, etc.).
      responses:
        '200':
          description: Audit log entries.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    uniqueId:
                      type: string
                    at:
                      type: integer
                      format: int64
                      description: Unix ms.
                    eventType:
                      type: string
                    actorAccountId:
                      type:
                        - string
                        - 'null'
                    clientId:
                      type:
                        - string
                        - 'null'
                    userId:
                      type:
                        - string
                        - 'null'
                    ip:
                      type:
                        - string
                        - 'null'
                    details:
                      type: object
                      additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Moderator
        - Fabricator
        - SuperUser
  /admin/oauth/clients/{uniqueId}/step-up/challenge:
    parameters:
      - in: path
        name: uniqueId
        required: true
        schema:
          type: string
    post:
      tags:
        - OAuth
      operationId: oauthStepUpChallenge
      summary: Send a step-up challenge code
      description: Emails a 6-digit code to the caller. The code is then submitted to `/step-up/verify` to open the unlock window.
      responses:
        '200':
          description: Challenge sent.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Moderator
        - Fabricator
        - SuperUser
  /admin/oauth/clients/{uniqueId}/step-up/verify:
    parameters:
      - in: path
        name: uniqueId
        required: true
        schema:
          type: string
    post:
      tags:
        - OAuth
      operationId: oauthStepUpVerify
      summary: Verify the step-up code
      description: Verifies the 6-digit code from `/step-up/challenge` and opens a brief unlock window during which the caller may mutate the client.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - code
              properties:
                code:
                  type: string
                  description: The 6-digit code from the challenge email.
      responses:
        '200':
          description: Step-up unlocked.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Moderator
        - Fabricator
        - SuperUser
  /admin/oauth/clients/audit:
    get:
      tags:
        - OAuth
      operationId: oauthGlobalAudit
      summary: Global OAuth audit log
      description: Returns the global OAuth audit log spanning every client. SuperUser-only.
      responses:
        '200':
          description: Global audit-log entries.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    uniqueId:
                      type: string
                    at:
                      type: integer
                      format: int64
                    eventType:
                      type: string
                    actorAccountId:
                      type:
                        - string
                        - 'null'
                    clientId:
                      type:
                        - string
                        - 'null'
                    userId:
                      type:
                        - string
                        - 'null'
                    ip:
                      type:
                        - string
                        - 'null'
                    details:
                      type: object
                      additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - SuperUser
  /admin/oauth/clients/{uniqueId}/hard_delete:
    parameters:
      - in: path
        name: uniqueId
        required: true
        schema:
          type: string
    post:
      tags:
        - OAuth
      operationId: oauthHardDeleteClient
      summary: Hard-delete an OAuth client
      description: Removes the client row entirely. Typically used on already-soft-deleted rows; also works on live rows. SuperUser-only.
      responses:
        '200':
          description: Client hard-deleted.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - SuperUser
  /admin/whoami:
    get:
      tags:
        - Accounts
      operationId: whoami
      summary: Current authenticated account
      description: |
        Returns sanitized account info for the caller — the JWT subject's profile.
        Accepts an OAuth bearer with `openid` scope as well as legacy app-key bearers.
      responses:
        '200':
          description: Whoami payload — different shape than the regular sanitized account.
          content:
            application/json:
              schema:
                type: object
                required:
                  - result
                  - accountId
                properties:
                  result:
                    type: string
                    example: successfully authenticated
                  accountId:
                    type: string
                  username:
                    type: string
                  via:
                    type: string
                    enum:
                      - oauth
                      - first-party
                    description: Auth method that admitted the request.
                  client:
                    type: string
                    description: OAuth client id (only when `via = oauth`).
                  scope:
                    type: string
                    description: OAuth scopes on the access token (only when `via = oauth`).
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - openid
  /admin/access/admin:
    get:
      tags:
        - Accounts
      operationId: accessAdmin
      summary: Probe — caller has Admin policy
      description: Returns `"OK"` if the caller has the `Admin` access policy, otherwise `403`. Used by arda to gate admin UI.
      responses:
        '200':
          description: Caller has Admin.
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - admin:all
  /admin/access/community:
    get:
      tags:
        - Accounts
      operationId: accessCommunity
      summary: Probe — caller has CommunityModerator policy
      responses:
        '200':
          description: Caller has CommunityModerator.
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - CommunityModerator
      x-scopes:
        - admin:all
  /admin/access/default:
    get:
      tags:
        - Accounts
      operationId: accessDefault
      summary: Probe — caller has BigscreenDefault policy
      responses:
        '200':
          description: Caller has BigscreenDefault.
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - BigscreenDefault
      x-scopes:
        - admin:all
  /admin/access/fabricator:
    get:
      tags:
        - Accounts
      operationId: accessFabricator
      summary: Probe — caller has Fabricator policy
      responses:
        '200':
          description: Caller has Fabricator.
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Fabricator
      x-scopes:
        - admin:all
  /admin/access/inventory:
    get:
      tags:
        - Accounts
      operationId: accessInventory
      summary: Probe — caller has Inventory policy
      responses:
        '200':
          description: Caller has Inventory.
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Inventory
      x-scopes:
        - admin:all
  /admin/access/moderator:
    get:
      tags:
        - Accounts
      operationId: accessModerator
      summary: Probe — caller has Moderator policy
      responses:
        '200':
          description: Caller has Moderator.
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - admin:all
  /admin/access/reporting:
    get:
      tags:
        - Accounts
      operationId: accessReporting
      summary: Probe — caller has Reporting policy
      responses:
        '200':
          description: Caller has Reporting.
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Reporting
      x-scopes:
        - admin:all
  /admin/access/superuser:
    get:
      tags:
        - Accounts
      operationId: accessSuperUser
      summary: Probe — caller has SuperUser policy
      responses:
        '200':
          description: Caller has SuperUser.
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - SuperUser
      x-scopes:
        - admin:all
  /admin/accounts/staff:
    get:
      tags:
        - Accounts
      operationId: getStaffAccounts
      summary: List staff accounts
      description: Returns every Bigscreen staff account.
      responses:
        '200':
          description: Array of sanitized staff account objects.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AdminAccount'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - accounts:read
        - admin:all
  /admin/account:
    post:
      tags:
        - Accounts
      operationId: createBasicAccount
      summary: Create an account with email/password
      description: Creates a new account by username + email + password. Used to bootstrap test accounts and integration accounts.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - username
                - email
                - password
              properties:
                username:
                  type: string
                email:
                  type: string
                  format: email
                  description: Validated and normalized.
                password:
                  type: string
                  minLength: 6
      responses:
        '200':
          description: Created account (sanitized).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdminAccount'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: Email already in use.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Validation error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - admin:all
  /admin/onboarding/account:
    post:
      tags:
        - Accounts
      operationId: createOnboardingAccount
      summary: Create a Bigscreen onboarding account
      description: |
        Creates a Bigscreen onboarding account. Email must be `@bigscreenvr.com`. A
        temporary password is generated server-side and returned in the response so
        the operator can hand it to the new staff member.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - username
                - email
              properties:
                username:
                  type: string
                email:
                  type: string
                  format: email
                  description: Must be `@bigscreenvr.com`.
      responses:
        '200':
          description: Created onboarding account, with the generated temporary password attached.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/AdminAccount'
                  - type: object
                    required:
                      - temporaryPassword
                    properties:
                      temporaryPassword:
                        type: string
                        description: Server-generated; hand off to the new staff member out-of-band.
        '400':
          description: Email is not `@bigscreenvr.com`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: Email already in use.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Validation error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - accounts:write
        - admin:all
  /admin/account/{bigscreenAccountId}/access_policy/{accessPolicy}:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
        description: Target account id.
      - in: path
        name: accessPolicy
        required: true
        schema:
          type: string
        description: One of `EditableAccessPolicies` (e.g. `Moderator`, `Fabricator`, `Inventory`).
    put:
      tags:
        - Accounts
      operationId: addAccessPolicyToAccount
      summary: Grant an access policy to an account
      description: Adds an access policy to an account. Target account must have a `@bigscreenvr.com` email.
      responses:
        '200':
          description: Policy added.
        '400':
          description: Invalid domain or account already has the policy.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Caller lacks permission.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Account not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - accounts:write
        - admin:all
    delete:
      tags:
        - Accounts
      operationId: removeAccessPolicyFromAccount
      summary: Revoke an access policy from an account
      description: Removes an access policy from an account. Target must have a `@bigscreenvr.com` email.
      responses:
        '200':
          description: Policy removed.
        '400':
          description: Invalid domain or account does not have the policy.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Caller lacks permission.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Account not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - accounts:write
        - admin:all
  /admin/account/{bigscreenAccountId}/security_groups:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    get:
      tags:
        - Accounts
      operationId: getSecurityGroupInfo
      summary: AWS security group entries for an account
      description: Returns AWS EC2 security group IP entries filtered to the supplied account.
      responses:
        '200':
          description: Filtered AWS security group payload.
          content:
            application/json:
              schema:
                type: object
                properties:
                  GroupId:
                    type: string
                  GroupName:
                    type: string
                  Description:
                    type: string
                  IpPermissions:
                    type: array
                    description: Per-rule permissions limited to entries tagged with this account.
                    items:
                      type: object
                      properties:
                        IpProtocol:
                          type: string
                        FromPort:
                          type: integer
                        ToPort:
                          type: integer
                        IpRanges:
                          type: array
                          items:
                            type: object
                            properties:
                              CidrIp:
                                type: string
                              Description:
                                type: string
                        Ipv6Ranges:
                          type: array
                          items:
                            type: object
                            properties:
                              CidrIpv6:
                                type: string
                              Description:
                                type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Account not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - accounts:read
        - admin:all
  /admin/account/{bigscreenAccountId}/security_groups/{ip}:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
      - in: path
        name: ip
        required: true
        schema:
          type: string
        description: Valid IPv4 or IPv6 address. `/32` (v4) or `/128` (v6) suffix is auto-appended.
    put:
      tags:
        - Accounts
      operationId: addSecurityGroupEntry
      summary: Allowlist an IP for an account
      description: Adds the supplied IP to the AWS security group, attributed to the supplied account.
      responses:
        '200':
          description: IP allowlisted.
        '400':
          description: IP already exists or account has no email.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Caller is not Admin.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Account not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - accounts:write
        - admin:all
    delete:
      tags:
        - Accounts
      operationId: removeSecurityGroupEntry
      summary: Remove an IP from an account's allowlist
      description: Removes the supplied IP from the AWS security group entry attributed to the supplied account.
      responses:
        '200':
          description: IP removed.
        '400':
          description: IP not found or account has no email.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Caller is not Admin.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Account not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - accounts:write
        - admin:all
  /admin/media/item:
    post:
      tags:
        - Media
      operationId: createMediaItem
      summary: Create a media item
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - title
                - cdn
                - cdnId
              properties:
                title:
                  type: string
                cdn:
                  type: string
                  description: CDN identifier (e.g. `cloudfront`, `bunny`).
                cdnId:
                  type: string
                type:
                  type: string
                  description: '`MediaType` value.'
                isStereo:
                  type: boolean
                stereoPacking:
                  type: string
                  enum:
                    - None
                    - LeftRight
                    - OverUnder
                stereoWidth:
                  type: string
                  enum:
                    - Default
                    - Half
                duration:
                  type: integer
                locationCodes:
                  type: array
                  items:
                    type: string
                audioFormats:
                  type: array
                  items:
                    type: string
                metaData:
                  type: object
                  additionalProperties: true
      responses:
        '200':
          description: Created media item.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaItem'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
    put:
      tags:
        - Media
      operationId: updateMediaItem
      summary: Update a media item
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
              properties:
                id:
                  type: string
                title:
                  type: string
                cdn:
                  type: string
                cdnId:
                  type: string
                type:
                  type: string
                isStereo:
                  type: boolean
                stereoPacking:
                  type: string
                stereoWidth:
                  type: string
                duration:
                  type: integer
                locationCodes:
                  type: array
                  items:
                    type: string
                audioFormats:
                  type: array
                  items:
                    type: string
                metaData:
                  type: object
                  additionalProperties: true
      responses:
        '200':
          description: Updated media item.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaItem'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/media/items:
    get:
      tags:
        - Media
      operationId: getMediaItems
      summary: List media items
      responses:
        '200':
          description: Array of media items.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/MediaItem'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/media/item/{id}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
    get:
      tags:
        - Media
      operationId: getMediaItem
      summary: Fetch a media item
      responses:
        '200':
          description: Media item.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaItem'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
    delete:
      tags:
        - Media
      operationId: deleteMediaItem
      summary: Delete a media item
      responses:
        '200':
          description: Deleted.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/media/cdn/{cdn}/{cdnId}:
    parameters:
      - in: path
        name: cdn
        required: true
        schema:
          type: string
        description: CDN name (e.g. `cloudfront`, `bunny`).
      - in: path
        name: cdnId
        required: true
        schema:
          type: string
        description: CDN-side identifier for the asset.
    get:
      tags:
        - Media
      operationId: getCDNInfo
      summary: CDN info for a media asset
      responses:
        '200':
          description: CDN info payload — passthrough from the CDN provider, shape depends on `cdn`.
          content:
            application/json:
              schema:
                type: object
                properties:
                  cdn:
                    type: string
                  cdnId:
                    type: string
                  duration:
                    type: integer
                    description: Asset duration in milliseconds.
                  resolution:
                    type: string
                    description: e.g. `1920x1080`.
                  bitrate:
                    type: integer
                  tracks:
                    type: object
                    properties:
                      audio:
                        type: array
                        items:
                          type: string
                      subtitle:
                        type: array
                        items:
                          type: string
                  raw:
                    type: object
                    additionalProperties: true
                    description: Raw provider-specific payload.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/media/schemas:
    get:
      tags:
        - Media
      operationId: getMediaSchemas
      summary: Media schema enumerations
      responses:
        '200':
          description: |
            Schema enumerations — `EntitlementClass`, `EntitlementStatus`,
            `MediaType`, `ProductStatus`, etc. Keys are the enum names; values are
            the string members.
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: array
                  items:
                    type: string
                description: Each top-level key is an enum name; the value is the list of legal members.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/media/query:
    post:
      tags:
        - Media
      operationId: getMediaQuery
      summary: Run a media query
      description: |
        Runs a free-form media query — body shape is whatever the handler accepts
        today (filters by id list, bigMediaId list, status, etc.). Result is keyed
        by the entity type returned (`mediaItems`, `mediaProducts`, …).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                ids:
                  type: array
                  items:
                    type: string
                bigMediaIds:
                  type: array
                  items:
                    type: string
                status:
                  type: string
                  description: '`ProductStatus` filter.'
                limit:
                  type: integer
                offset:
                  type: integer
              additionalProperties: true
      responses:
        '200':
          description: Query result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  mediaItems:
                    type: array
                    items:
                      $ref: '#/components/schemas/MediaItem'
                  mediaProducts:
                    type: array
                    items:
                      $ref: '#/components/schemas/MediaProduct'
                  count:
                    type: integer
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/media/product:
    post:
      tags:
        - Media
      operationId: createProduct
      summary: Create a media product
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - title
              properties:
                title:
                  type: string
                description:
                  type: string
                bigMediaId:
                  type: string
                  description: Public id; auto-generated when blank.
                status:
                  type: string
                  description: '`ProductStatus` value.'
                mediaItemIds:
                  type: array
                  items:
                    type: string
                entitlementClass:
                  type: string
                synchronizationInterval:
                  type: integer
                  minimum: 0
                  maximum: 30
                locationCodes:
                  type: array
                  items:
                    type: string
                is3D:
                  type: boolean
                loops:
                  type: boolean
                duration:
                  type: integer
                marketingData:
                  type: object
                  additionalProperties: true
      responses:
        '200':
          description: Created product.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaProduct'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
    put:
      tags:
        - Media
      operationId: updateProduct
      summary: Update a media product
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
              properties:
                id:
                  type: string
                title:
                  type: string
                description:
                  type: string
                status:
                  type: string
                mediaItemIds:
                  type: array
                  items:
                    type: string
                entitlementClass:
                  type: string
                synchronizationInterval:
                  type: integer
                locationCodes:
                  type: array
                  items:
                    type: string
                is3D:
                  type: boolean
                loops:
                  type: boolean
                duration:
                  type: integer
                marketingData:
                  type: object
                  additionalProperties: true
      responses:
        '200':
          description: Updated product.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaProduct'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/media/product/{id}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
    get:
      tags:
        - Media
      operationId: getProduct
      summary: Fetch a media product
      responses:
        '200':
          description: Product record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaProduct'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
    delete:
      tags:
        - Media
      operationId: deleteProduct
      summary: Delete a media product
      responses:
        '200':
          description: Product deleted.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/media/products:
    get:
      tags:
        - Media
      operationId: getProducts
      summary: List media products
      responses:
        '200':
          description: Array of products.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/MediaProduct'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/media/product/{id}/sku:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
        description: Product id.
    post:
      tags:
        - Media
      operationId: createProductSKU
      summary: Create a SKU on a product
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                id:
                  type: string
                  description: Stripe / Oculus IAP SKU id.
                price:
                  type: number
                currency:
                  type: string
                  example: USD
                entitlementClass:
                  type: string
              additionalProperties: true
      responses:
        '200':
          description: Created SKU.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  price:
                    type: number
                  currency:
                    type: string
                  entitlementClass:
                    type: string
                  productId:
                    type: string
                  createdAt:
                    type: string
                    format: date-time
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/media/product/{id}/sku/{skuId}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
      - in: path
        name: skuId
        required: true
        schema:
          type: string
    delete:
      tags:
        - Media
      operationId: deleteProductSKU
      summary: Delete a SKU on a product
      responses:
        '200':
          description: SKU deleted.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/media/showings/{synchronizationInterval}/{past}/{future}:
    parameters:
      - in: path
        name: synchronizationInterval
        required: true
        schema:
          type: string
        description: Synchronization interval identifier.
      - in: path
        name: past
        required: true
        schema:
          type: string
        description: |
          How far back to look (e.g. number of intervals). Optional in the underlying
          route (`/showings/:synchronizationInterval{/:past}{/:future}`) — when omitted,
          defaults are applied server-side.
      - in: path
        name: future
        required: true
        schema:
          type: string
        description: How far forward to look. Optional in the underlying route.
    get:
      tags:
        - Media
      operationId: getShowings
      summary: Media showings for a time window
      description: Returns scheduled media showings within the supplied time window. The trailing `past` and `future` segments are optional in the underlying route.
      responses:
        '200':
          description: Showings payload.
          content:
            application/json:
              schema:
                type: object
                properties:
                  synchronizationInterval:
                    type: string
                  showings:
                    type: array
                    items:
                      type: object
                      properties:
                        mediaProductId:
                          type: string
                        bigMediaId:
                          type: string
                        startTimestamp:
                          type: string
                          format: date-time
                        endTimestamp:
                          type: string
                          format: date-time
                        durationMs:
                          type: integer
                      additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/media/events/{productId}:
    parameters:
      - in: path
        name: productId
        required: true
        schema:
          type: string
    get:
      tags:
        - Media
      operationId: getMediaSessionEvents
      summary: Media session events for a product
      description: Returns media session events (plays, completions, abandons) for a specific product.
      responses:
        '200':
          description: Events payload.
          content:
            application/json:
              schema:
                type: object
                properties:
                  productId:
                    type: string
                  events:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        eventType:
                          type: string
                          description: e.g. `play`, `complete`, `abandon`.
                        bigscreenAccountId:
                          type: string
                        timestamp:
                          type: string
                          format: date-time
                        durationMs:
                          type: integer
                      additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/media/products/export:
    get:
      tags:
        - Media
      operationId: exportMediaProductsTSV
      summary: Export media products as TSV
      description: Streams a TSV of all media products. Auth requirement is stricter than the default Admin-only — also accepts SuperUser.
      responses:
        '200':
          description: TSV stream.
          content:
            text/tab-separated-values:
              schema:
                type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - SuperUser
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/media/entitlement:
    post:
      tags:
        - Media
      operationId: adminCreateEntitlement
      summary: Create an entitlement
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - bigscreenAccountId
                - mediaProductId
                - entitlementClass
              properties:
                bigscreenAccountId:
                  type: string
                mediaProductId:
                  type: string
                bigMediaId:
                  type: string
                entitlementClass:
                  type: string
                maxAllowedSessions:
                  type: integer
                activationTimestamp:
                  type: string
                  format: date-time
                expirationTimestamp:
                  type: string
                  format: date-time
              additionalProperties: true
      responses:
        '200':
          description: Created entitlement.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Entitlement'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/media/entitlement/{id}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
    put:
      tags:
        - Media
      operationId: adminUpdateEntitlement
      summary: Update an entitlement
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                status:
                  type: string
                expirationTimestamp:
                  type: string
                  format: date-time
                maxAllowedSessions:
                  type: integer
              additionalProperties: true
      responses:
        '200':
          description: Updated entitlement.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Entitlement'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
    get:
      tags:
        - Media
      operationId: getEntitlement
      summary: Fetch an entitlement
      responses:
        '200':
          description: Entitlement record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Entitlement'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/media/entitlements/query:
    post:
      tags:
        - Media
      operationId: searchEntitlements
      summary: Search entitlements
      description: Searches entitlements with a query body.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                bigscreenAccountId:
                  type: string
                mediaProductId:
                  type: string
                bigMediaId:
                  type: string
                entitlementClass:
                  type: string
                status:
                  type: string
                limit:
                  type: integer
                offset:
                  type: integer
              additionalProperties: true
      responses:
        '200':
          description: Matching entitlements.
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/Entitlement'
                  count:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/media/entitlements:
    get:
      tags:
        - Media
      operationId: getEntitlements
      summary: List entitlements
      responses:
        '200':
          description: Array of entitlements.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Entitlement'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/media/entitlements/export:
    get:
      tags:
        - Media
      operationId: exportEntitlementsCSV
      summary: Export entitlements as CSV
      description: Streams a CSV of every entitlement. SuperUser-only.
      responses:
        '200':
          description: CSV stream.
          content:
            text/csv:
              schema:
                type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - SuperUser
      x-scopes:
        - media:read
        - admin:all
  /admin/media/category:
    post:
      tags:
        - Media
      operationId: adminCreateCategory
      summary: Create a category
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - title
              properties:
                title:
                  type: string
                description:
                  type: string
                entitlementClass:
                  type: string
                isScheduled:
                  type: boolean
              additionalProperties: true
      responses:
        '200':
          description: Created category.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaCategory'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
    put:
      tags:
        - Media
      operationId: adminUpdateCategory
      summary: Update a category
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
              properties:
                id:
                  type: string
              additionalProperties: true
      responses:
        '200':
          description: Updated category.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaCategory'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
    get:
      tags:
        - Media
      operationId: adminGetCategory
      summary: Fetch a single category
      description: Returns a single category. The id is supplied via query string.
      responses:
        '200':
          description: Category record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaCategory'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/media/categories:
    get:
      tags:
        - Media
      operationId: adminGetCategories
      summary: List categories
      responses:
        '200':
          description: Array of categories.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/MediaCategory'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/media/category/{id}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
    delete:
      tags:
        - Media
      operationId: deleteModeratorCategory
      summary: Delete a category
      responses:
        '200':
          description: Category deleted.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/media/collection:
    post:
      tags:
        - Media
      operationId: adminCreateProductCollection
      summary: Create a product collection
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
              properties:
                name:
                  type: string
                startTimestamp:
                  type: string
                  format: date-time
                endTimestamp:
                  type: string
                  format: date-time
                mediaProductIds:
                  type: array
                  items:
                    type: string
                isPublished:
                  type: boolean
              additionalProperties: true
      responses:
        '200':
          description: Created collection.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductCollection'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
    put:
      tags:
        - Media
      operationId: adminUpdateProductCollection
      summary: Update a product collection
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
              properties:
                id:
                  type: string
              additionalProperties: true
      responses:
        '200':
          description: Updated collection.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductCollection'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/media/collections:
    get:
      tags:
        - Media
      operationId: adminGetProductCollections
      summary: List product collections
      responses:
        '200':
          description: Array of collections.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProductCollection'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/media/collection/{id}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
    get:
      tags:
        - Media
      operationId: adminGetProductCollection
      summary: Fetch a product collection
      responses:
        '200':
          description: Collection record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductCollection'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
    delete:
      tags:
        - Media
      operationId: deleteModeratorProductCollection
      summary: Delete a product collection
      responses:
        '200':
          description: Collection deleted.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/media/coupons:
    post:
      tags:
        - Media
      operationId: generateCoupons
      summary: Generate coupons
      description: Generates a batch of coupons.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                count:
                  type: integer
                  description: Number of coupons to mint.
                entitlementClass:
                  type: string
                bigMediaId:
                  type: string
                  description: Optional — restrict to a specific media product.
                expirationTimestamp:
                  type: string
                  format: date-time
              additionalProperties: true
      responses:
        '200':
          description: Generated coupons.
          content:
            application/json:
              schema:
                type: object
                properties:
                  coupons:
                    type: array
                    items:
                      $ref: '#/components/schemas/Coupon'
                  count:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
    get:
      tags:
        - Media
      operationId: getCoupons
      summary: List coupons
      responses:
        '200':
          description: Array of coupons.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Coupon'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/media/coupon:
    put:
      tags:
        - Media
      operationId: updateCoupon
      summary: Update a coupon
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
              properties:
                id:
                  type: string
                expirationTimestamp:
                  type: string
                  format: date-time
              additionalProperties: true
      responses:
        '200':
          description: Updated coupon.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Coupon'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/media/coupon/{id}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
    get:
      tags:
        - Media
      operationId: getCoupon
      summary: Fetch a coupon
      responses:
        '200':
          description: Coupon record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Coupon'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
    delete:
      tags:
        - Media
      operationId: deleteCoupon
      summary: Delete a coupon
      responses:
        '200':
          description: Coupon deleted.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:write
        - admin:all
  /admin/media/rebuild/category/{id}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
    get:
      tags:
        - Media
      operationId: rebuildCategoryTextures
      summary: Rebuild category textures
      description: Triggers a texture rebuild for the supplied category.
      responses:
        '200':
          description: Rebuild result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  categoryId:
                    type: string
                  texturesRebuilt:
                    type: integer
                  durationMs:
                    type: integer
                  errors:
                    type: array
                    items:
                      type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/media/rebuild/skus:
    get:
      tags:
        - Media
      operationId: rebuildProductSkus
      summary: Rebuild all product SKUs
      description: Triggers a rebuild of every product SKU.
      responses:
        '200':
          description: Rebuild result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  productsProcessed:
                    type: integer
                  skusRebuilt:
                    type: integer
                  durationMs:
                    type: integer
                  errors:
                    type: array
                    items:
                      type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - media:read
        - admin:all
  /admin/inventory/hint:
    get:
      tags:
        - Inventory
      operationId: getInventoryHint
      summary: Identify a scanned serial number
      description: |
        Given a serial number or barcode, returns a hint about what kind of inventory
        item it represents. Used at factory scanning stations before the operator
        has chosen a destination workflow — the hint determines which UI to show.
      parameters:
        - in: query
          name: serialNumber
          required: true
          schema:
            type: string
          description: The serial number or barcode to identify.
          example: BB-2024-001234
      responses:
        '200':
          description: Hint resolved.
          content:
            application/json:
              schema:
                type: object
                required:
                  - type
                  - productType
                  - isValid
                properties:
                  type:
                    type: string
                    description: Coarse category — e.g. `"beyond"`, `"cushion"`, `"lens"`.
                    example: beyond
                  productType:
                    $ref: '#/components/schemas/BigProductType'
                  isValid:
                    type: boolean
                    description: Whether the serial number conforms to a known format.
                  message:
                    type: string
                    description: Optional human-readable note (e.g. "valid Beyond serial").
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - admin:all
        - inventory:read
  /admin/inventory/items:
    get:
      tags:
        - Inventory
      operationId: listInventoryItems
      summary: List inventory items with filters and pagination
      description: |
        Returns inventory items matching the supplied filters. Most filters are exact
        matches; `serialNumberLike` does a substring match and `prefixSerialNumber` does
        a prefix match. Pagination uses `limit` / `offset` (no cursor).

        **Performance note.** With no filters this scans the entire `big_inventory_items`
        table. Always supply at least one of `status`, `type`, `bigShipmentId`, or
        `jobId` for production queries.
      parameters:
        - in: query
          name: type
          schema:
            $ref: '#/components/schemas/BigProductType'
          description: Filter by product type.
        - in: query
          name: serialNumber
          schema:
            type: string
          description: Exact serial-number match.
        - in: query
          name: serialNumberLike
          schema:
            type: string
          description: Substring match against serial number.
        - in: query
          name: prefixSerialNumber
          schema:
            type: string
          description: Prefix match against serial number.
        - in: query
          name: status
          schema:
            $ref: '#/components/schemas/BigInventoryItemStatus'
        - in: query
          name: statusNot
          schema:
            $ref: '#/components/schemas/BigInventoryItemStatus'
          description: Exclude items in this status.
        - in: query
          name: trackingType
          schema:
            $ref: '#/components/schemas/BigInventoryTrackingType'
        - in: query
          name: bigShipmentId
          schema:
            type: string
        - in: query
          name: jobId
          schema:
            type: string
        - in: query
          name: minCreatedAt
          schema:
            type: integer
            format: int64
          description: Minimum `createdAt` (Unix ms).
        - in: query
          name: maxCreatedAt
          schema:
            type: integer
            format: int64
        - in: query
          name: limit
          schema:
            type: integer
            default: 100
            maximum: 1000
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
        - in: query
          name: sortBy
          schema:
            type: string
            default: createdAt
        - in: query
          name: sortDirection
          schema:
            type: string
            enum:
              - asc
              - desc
            default: desc
        - in: query
          name: createdAfter
          schema:
            type: string
            format: date-time
          description: ISO-8601 timestamp; alternative to `minCreatedAt`.
        - in: query
          name: createdBefore
          schema:
            type: string
            format: date-time
      responses:
        '200':
          description: Page of inventory items.
          content:
            application/json:
              schema:
                type: object
                required:
                  - items
                  - total
                  - limit
                  - offset
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/BigInventoryItem'
                  total:
                    type: integer
                    description: Total matching the filter (before pagination).
                  limit:
                    type: integer
                  offset:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - admin:all
        - inventory:read
  /admin/inventory/stats:
    get:
      tags:
        - Inventory
      operationId: getInventoryStats
      summary: Inventory counts by type and status
      description: |
        Aggregate counts of inventory items grouped by `BigProductType` and
        `BigInventoryItemStatus`. Cheap to call (`COUNT(*) … GROUP BY …`) — safe
        for dashboards that poll every few seconds.
      responses:
        '200':
          description: Aggregate counts.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                description: |
                  Shape returned by `InventoryAdminApi.getInventoryStats`. Top-level keys
                  are product types; values are counts-by-status objects. The exact key
                  set tracks `BigProductType` and `BigInventoryItemStatus` enums.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - admin:all
        - inventory:read
  /admin/inventory/item:
    post:
      tags:
        - Inventory
      operationId: createInventoryItem
      summary: Create an inventory item
      description: |
        Creates a single inventory item. The exact required fields depend on
        `trackingType`:

        - **`Personalized`** — `serialNumber` is required; `quantity` is forced to `1`.
        - **`QuantityTracked`** — `productType` and `quantity` are required; `serialNumber`
          is optional and represents a batch identifier.
        - **`Untracked`** — `productType` is required; `serialNumber` and `quantity` are ignored.

        Items are typically created by the manufacturing pipeline (a finished cushion comes
        off the line, scans its NFC tag, and an item is created). Manual creation here is
        used for accessories, restocks, and test setup.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BigInventoryItemCreationParams'
            examples:
              personalizedCushion:
                summary: Personalized cushion off the line
                value:
                  serialNumber: BC-2025-007842
                  productType: BeyondCushionV1
                  trackingType: Personalized
                  jobId: job_01HXY8ZW7Q9V0K3M5RPQ4ABCDE
              untrackedAccessory:
                summary: Restocking soft straps
                value:
                  productType: BeyondSoftStrapV1
                  trackingType: QuantityTracked
                  quantity: 200
      responses:
        '200':
          description: The created inventory item.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BigInventoryItem'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          description: Validation error — missing required fields for the chosen `trackingType`, or invalid `productType`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - admin:all
        - inventory:write
  /admin/inventory/item/{id}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
        description: The inventory item id.
    get:
      tags:
        - Inventory
      operationId: getInventoryItem
      summary: Fetch one inventory item
      description: |
        Returns a single inventory item by id, with its embedded `history` array.
        `history` is appended to on every state-machine transition and includes the
        acting account id where applicable.
      responses:
        '200':
          description: The inventory item.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BigInventoryItem'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No item with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - admin:all
        - inventory:read
    put:
      tags:
        - Inventory
      operationId: updateInventoryItem
      summary: Update an item's quantity
      description: |
        Updates the `quantity` field on a `QuantityTracked` inventory item. This endpoint
        does **not** mutate `status` — that is driven by the shipping pipeline. To change
        state manually, use the relevant pipeline endpoint (`PUT /admin/shipper/shipment/:id`
        with the appropriate `action`).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - quantity
              properties:
                quantity:
                  type: integer
                  minimum: 0
                  description: New quantity. Must be a non-negative integer.
                  example: 150
      responses:
        '200':
          description: The updated item.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BigInventoryItem'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No item with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Invalid `quantity` (negative or non-integer).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - admin:all
        - inventory:write
    delete:
      tags:
        - Inventory
      operationId: deleteInventoryItem
      summary: Delete an inventory item
      description: |
        Soft-deletes the item by transitioning its status to `Deleted` (the row stays
        in the table). Pass `?force=1` to hard-delete the row entirely; this requires
        an elevated access policy (`FabricatorAdmin`, `Admin`, or `SuperUser`) and is
        not recoverable.

        Soft-deleted items are excluded from `GET /admin/inventory/items` and
        `/stats` by default. They remain visible to `GET /admin/inventory/item/{id}`
        so the audit history is not lost.
      parameters:
        - in: query
          name: force
          schema:
            type: string
            enum:
              - '1'
          description: |
            If set to `"1"`, hard-deletes the row instead of soft-deleting. Requires
            `FabricatorAdmin`, `Admin`, or `SuperUser`.
      responses:
        '200':
          description: Deletion result.
          content:
            application/json:
              schema:
                type: object
                required:
                  - success
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
              examples:
                soft:
                  summary: Soft delete
                  value:
                    success: true
                    message: Inventory item soft-deleted
                hard:
                  summary: Hard delete with force=1
                  value:
                    success: true
                    message: Inventory item deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Caller lacks the elevated policy required for `force=1`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: No item with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - Inventory
        - FabricatorAdmin
      x-scopes:
        - admin:all
        - inventory:write
  /admin/inventory/item/{id}/history:
    get:
      tags:
        - Inventory
      operationId: getInventoryItemHistory
      summary: Per-item audit history
      description: |
        Returns every row from the `big_inventory_item_history` table for this item,
        most-recent-first. There is no pagination — operators reviewing a single
        cushion's history want the full timeline.

        Distinct from the `history` array embedded on the item itself: that one is a
        truncated snapshot for display, while this endpoint reads the underlying audit
        table verbatim.
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Array of raw history rows.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  description: |
                    Raw row from `big_inventory_item_history`. The handler returns
                    `SELECT *` — new columns surface here without a code change.
                  properties:
                    id:
                      type: string
                    bigInventoryItemId:
                      type: string
                    createdAt:
                      type: string
                      format: date-time
                  additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          description: Empty `id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - admin:all
        - inventory:read
  /admin/inventory/report:
    get:
      tags:
        - Inventory
      operationId: getInventoryReport
      summary: Comprehensive inventory report
      description: |
        Generates a full inventory dashboard: per-product-type and per-status counts,
        today's activity, and low-stock warnings. Heavier than `/stats` — fan-outs to
        multiple aggregations and is intended for the daily-check Looper job, not
        interactive polling.
      responses:
        '200':
          description: Report payload.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InventoryReport'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - FabricatorReadOnly
      x-scopes:
        - admin:all
        - inventory:read
  /admin/inventory/sync:
    get:
      tags:
        - Inventory
      operationId: syncInventory
      summary: Reconcile inventory from completed jobs
      description: |
        Walks fabricator jobs in state `AwaitingFulfilment` and creates the matching
        inventory items if they don't already exist. Idempotent — items already linked
        to a job are skipped.

        Used as a recovery path when an item failed to be created automatically at the
        end of a job (rare; usually triggered by a process crash mid-flow).
      responses:
        '200':
          description: Sync result — items created and skipped, plus any errors.
          content:
            application/json:
              schema:
                type: object
                properties:
                  created:
                    type: integer
                  skipped:
                    type: integer
                  errors:
                    type: array
                    items:
                      type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - admin:all
  /admin/dhl/invoices:
    get:
      tags:
        - DHL
      operationId: getDHLInvoices
      summary: List DHL invoices
      description: Returns DHL invoices with optional filtering.
      parameters:
        - in: query
          name: status
          schema:
            type: string
          description: Invoice status filter.
        - in: query
          name: invoiceNumber
          schema:
            type: string
          description: Invoice number filter.
        - in: query
          name: limit
          schema:
            type: integer
        - in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: Invoices page.
          content:
            application/json:
              schema:
                type: object
                required:
                  - items
                  - count
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/DHLInvoice'
                  count:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:read
        - admin:all
  /admin/dhl/invoices/summary:
    get:
      tags:
        - DHL
      operationId: getDHLInvoiceSummary
      summary: Aggregate totals across invoices + charges
      description: |
        Returns aggregate totals across the invoice + charge tables — used by Arda's
        DHL dashboard. **Note:** the handler swallows DB errors and returns all-zeros;
        check server logs if you see all-zero totals before assuming the DB is empty.
      responses:
        '200':
          description: Summary payload.
          content:
            application/json:
              schema:
                type: object
                required:
                  - invoiceCount
                  - totalValue
                  - totalChargeCount
                properties:
                  invoiceCount:
                    type: integer
                    description: COUNT(*) on `big_dhl_invoices`.
                  totalValue:
                    type: number
                    description: SUM(totalAmount) on `big_dhl_invoices`, COALESCEd to 0.
                  totalChargeCount:
                    type: integer
                    description: COUNT(*) on `big_dhl_charges`.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:read
        - admin:all
  /admin/dhl/invoices/{id}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
    get:
      tags:
        - DHL
      operationId: getDHLInvoice
      summary: Fetch a DHL invoice
      description: Returns a single DHL invoice by id.
      responses:
        '200':
          description: Invoice record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DHLInvoice'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No invoice with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:read
        - admin:all
  /admin/dhl/invoices/upload-csv:
    post:
      tags:
        - DHL
      operationId: uploadDHLCSV
      summary: Ingest a DHL MyBill CSV
      description: |
        Ingests a DHL MyBill CSV export. Creates or updates invoices **and** their
        per-tracking-number charges in one pass. This is the only way to get DHL data
        into the system — there is no live Shippo API sync.

        Header matching is case-insensitive and strips non-alphanumeric characters
        (`Invoice Number` ≡ `invoice_number` ≡ `InvoiceNumber`). Rows without
        `invoiceNumber` are skipped (added to the response's `errors` array, not
        rejected). Duplicate `trackingNumber` values within a single CSV collapse
        into a single charge row (last-write-wins).
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
              properties:
                file:
                  type: string
                  format: binary
                  description: CSV file from DHL MyBill.
      responses:
        '200':
          description: Ingestion summary.
          content:
            application/json:
              schema:
                type: object
                required:
                  - invoicesCreated
                  - invoicesUpdated
                  - chargesCreated
                  - chargesUpdated
                  - errors
                properties:
                  invoicesCreated:
                    type: integer
                    description: New `DHLInvoice` rows.
                  invoicesUpdated:
                    type: integer
                    description: Existing `DHLInvoice` rows updated.
                  chargesCreated:
                    type: integer
                    description: New `DHLCharge` rows.
                  chargesUpdated:
                    type: integer
                    description: Existing `DHLCharge` rows updated (upsert key `(invoiceId, trackingNumber)`).
                  errors:
                    type: array
                    items:
                      type: string
                    description: Per-row non-fatal errors.
        '400':
          description: No file uploaded, parse error, no data rows, or missing invoice number column.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:write
        - admin:all
  /admin/dhl/charges:
    get:
      tags:
        - DHL
      operationId: getDHLCharges
      summary: Browse charges
      description: Returns charges across invoices with paging and tracking-number substring search.
      parameters:
        - in: query
          name: invoiceId
          schema:
            type: string
          description: Exact match on `invoiceId`.
        - in: query
          name: trackingNumber
          schema:
            type: string
          description: ILIKE substring match (case-insensitive).
        - in: query
          name: invoiceNumber
          schema:
            type: string
          description: Exact match.
        - in: query
          name: limit
          schema:
            type: integer
            default: 100
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Charges page.
          content:
            application/json:
              schema:
                type: object
                required:
                  - items
                  - count
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/DHLCharge'
                  count:
                    type: integer
                    description: Total matching rows (via `COUNT(*) OVER()`).
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:read
        - admin:all
  /admin/dhl/charges/flagged:
    get:
      tags:
        - DHL
      operationId: getDHLFlaggedCharges
      summary: List flagged charges
      description: Returns flagged charges with optional filtering. Flags are created by `POST /admin/dhl/reconcile`.
      parameters:
        - in: query
          name: invoiceId
          schema:
            type: string
        - in: query
          name: status
          schema:
            type: string
        - in: query
          name: trackingNumber
          schema:
            type: string
        - in: query
          name: limit
          schema:
            type: integer
        - in: query
          name: offset
          schema:
            type: integer
      responses:
        '200':
          description: Flagged charges page.
          content:
            application/json:
              schema:
                type: object
                required:
                  - items
                  - count
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/DHLChargeFlag'
                  count:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:read
        - admin:all
  /admin/dhl/charges/{id}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
    put:
      tags:
        - DHL
      operationId: updateDHLChargeFlag
      summary: Update a charge flag
      description: Updates a charge flag's status and/or notes.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                status:
                  type: string
                  enum:
                    - New
                    - Reviewed
                    - Resolved
                    - Disputed
                notes:
                  type: string
      responses:
        '200':
          description: Updated flag.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DHLChargeFlag'
        '400':
          description: Invalid `status`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No flag with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:write
        - admin:all
  /admin/dhl/reconcile:
    post:
      tags:
        - DHL
      operationId: reconcileDHLInvoice
      summary: Reconcile an invoice against quoted rates
      description: |
        Reconciles an invoice by comparing each charge against the quoted shipment rate
        captured when the label was bought. Creates `DHLChargeFlag` rows for discrepancies.
        Idempotent — deletes existing flags for the invoice before recomputing.

        Flag reasons: `TrackingNotFound` | `NoQuotedRate` | `OverThreshold`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - invoiceId
              properties:
                invoiceId:
                  type: string
                threshold:
                  type: number
                  description: Discrepancy threshold; default `0` (flag any difference).
      responses:
        '200':
          description: Reconciliation summary.
          content:
            application/json:
              schema:
                type: object
                required:
                  - invoiceId
                  - invoiceNumber
                  - chargesProcessed
                  - flagsCreated
                properties:
                  invoiceId:
                    type: string
                  invoiceNumber:
                    type: string
                  chargesProcessed:
                    type: integer
                  flagsCreated:
                    type: integer
        '400':
          description: Missing `invoiceId`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Invoice not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:write
        - admin:all
  /admin/dhl/all:
    delete:
      tags:
        - DHL
      operationId: clearAllDHLData
      summary: Wipe every DHL invoice, charge, and flag
      description: |
        **Destructive and irreversible.** Deletes every DHL invoice, charge, and flag
        in a single transaction (flags → charges → invoices, rolled back on any
        failure). Intended for test resets and dev environments. Stricter auth than
        the rest of `/admin/dhl/*` — `Inventory` cannot call this.
      responses:
        '200':
          description: Wipe summary.
          content:
            application/json:
              schema:
                type: object
                required:
                  - flagsDeleted
                  - chargesDeleted
                  - invoicesDeleted
                properties:
                  flagsDeleted:
                    type: integer
                  chargesDeleted:
                    type: integer
                  invoicesDeleted:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - shipping:write
        - admin:all
  /admin/shipper/next:
    get:
      tags:
        - Shipping
      operationId: findNextShippableBigOrder
      summary: Find the next shippable Big Order
      description: |
        Returns the next Big Order ready to ship. Checks shipment groups to ensure
        all items in a group are ready before suggesting an order.
      parameters:
        - in: query
          name: limit
          schema:
            type: integer
            maximum: 1000
          description: Max results.
      responses:
        '200':
          description: Next shippable Big Order with metadata.
          content:
            application/json:
              schema:
                type: object
                properties:
                  bigOrder:
                    $ref: '#/components/schemas/BigOrder'
                  readyToShip:
                    type: boolean
                  reason:
                    type: string
                    description: Why this order was selected (or skipped).
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No orders are ready to ship.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:read
        - admin:all
  /admin/shipper/next3:
    get:
      tags:
        - Shipping
      operationId: findNextShippableBigOrder3
      summary: Alias for /admin/shipper/next
      description: Identical to `GET /admin/shipper/next`.
      parameters:
        - in: query
          name: limit
          schema:
            type: integer
            maximum: 1000
      responses:
        '200':
          description: Next shippable Big Order with metadata.
          content:
            application/json:
              schema:
                type: object
                properties:
                  bigOrder:
                    $ref: '#/components/schemas/BigOrder'
                  readyToShip:
                    type: boolean
                  reason:
                    type: string
                    description: Why this order was selected (or skipped).
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No orders are ready to ship.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:read
        - admin:all
  /admin/shipper/stats:
    get:
      tags:
        - Shipping
      operationId: getBigShipperStats
      summary: Shipping system stats
      description: Returns statistics about the shipping system — shipment counts in various states, average processing times, oldest active shipment age, etc.
      responses:
        '200':
          description: Stats payload.
          content:
            application/json:
              schema:
                type: object
                properties:
                  totalShipments:
                    type: integer
                  activeShipments:
                    type: integer
                  shippedToday:
                    type: integer
                  shippedThisWeek:
                    type: integer
                  shippedThisMonth:
                    type: integer
                  byStatus:
                    type: object
                    description: Count of shipments by `BigShipmentStatus`.
                    properties:
                      Init:
                        type: integer
                      Picking:
                        type: integer
                      ReadyToPack:
                        type: integer
                      Packing:
                        type: integer
                      WaitingForPickup:
                        type: integer
                      Shipped:
                        type: integer
                      Cancelled:
                        type: integer
                  averageProcessingTimeMinutes:
                    type: number
                  oldestActiveShipmentAge:
                    type: number
                    description: Age in minutes.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:read
        - admin:all
  /admin/shipper/shipments:
    get:
      tags:
        - Shipping
      operationId: getBigShipments
      summary: Query shipments
      description: Returns shipments matching the filter criteria.
      parameters:
        - in: query
          name: status
          schema:
            type: string
          description: Shipment status filter.
        - in: query
          name: shopifyOrderId
          schema:
            type: string
        - in: query
          name: bigOrderId
          schema:
            type: string
        - in: query
          name: limit
          schema:
            type: integer
            default: 100
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
        - in: query
          name: sortBy
          schema:
            type: string
            default: createdAt
        - in: query
          name: sortDirection
          schema:
            type: string
            enum:
              - asc
              - desc
            default: desc
        - in: query
          name: createdAfter
          schema:
            type: string
            format: date-time
        - in: query
          name: createdBefore
          schema:
            type: string
            format: date-time
      responses:
        '200':
          description: Shipments page.
          content:
            application/json:
              schema:
                type: object
                required:
                  - shipments
                  - total
                  - limit
                  - offset
                properties:
                  shipments:
                    type: array
                    items:
                      $ref: '#/components/schemas/BigShipment'
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:read
        - admin:all
  /admin/shipper/shipments/stats:
    get:
      tags:
        - Shipping
      operationId: getBigShipmentsStats
      summary: Shipment stats
      responses:
        '200':
          description: Shipment stats.
          content:
            application/json:
              schema:
                type: object
                properties:
                  totalShipments:
                    type: integer
                  byStatus:
                    type: object
                    additionalProperties:
                      type: integer
                  averageProcessingTimeMinutes:
                    type: number
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:read
        - admin:all
  /admin/shipper/shipments/status:
    get:
      tags:
        - Shipping
      operationId: getShipmentsWithCarrierStatus
      summary: Shipments with carrier tracking status
      description: Returns shipments paired with their current carrier tracking status.
      responses:
        '200':
          description: Shipments paired with carrier status.
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      type: object
                      properties:
                        shipment:
                          $ref: '#/components/schemas/BigShipment'
                        trackingStatus:
                          type: string
                          description: Carrier-reported status (e.g. `in_transit`, `delivered`, `failure`).
                        trackingNumber:
                          type: string
                        carrier:
                          type: string
                        lastUpdatedAt:
                          type: string
                          format: date-time
                  count:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:read
        - admin:all
  /admin/shipper/shipment:
    post:
      tags:
        - Shipping
      operationId: createBigShipment
      summary: Create a shipment for a Big Order
      description: Creates a new shipment for a Big Order. If `lineItemIds` is omitted, all line items are included.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - bigOrderId
              properties:
                bigOrderId:
                  type: string
                lineItemIds:
                  type: array
                  items:
                    type: string
      responses:
        '200':
          description: Created shipment.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BigShipment'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:write
        - admin:all
  /admin/shipper/shipment/{id}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
    get:
      tags:
        - Shipping
      operationId: getBigShipmentExtended
      summary: Fetch a shipment with extended info
      description: Returns a shipment with embedded inventory items, history, and the associated Big Order.
      responses:
        '200':
          description: Shipment record (extended).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BigShipment'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No shipment with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:read
        - admin:all
    put:
      tags:
        - Shipping
      operationId: updateBigShipment
      summary: RPC on a shipment (state-machine action)
      description: |
        Run a remote-procedure call on an existing shipment. Shipments operate in a
        state machine — the available `action` depends on the current status. Common
        actions include `Init`, `PickInventoryItem`, `FinishPicking`,
        `CalculateShippingRates`, `GenerateShippingLabel`, `StartPacking`,
        `VerifyInventoryItem`, `FinishPacking`, `ShipIt`, `Cancel`,
        `ForceWaitingForPickup`, `ForceShipped`, `RegenerateShippingLabel`,
        `UpdateCarrierStatus`, `Update`, `ForceRemovePickedInventoryItem`. See the
        shipping module's markdown for action details.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
                - action
              properties:
                id:
                  type: string
                  description: Shipment id (must match the path).
                action:
                  type: string
                  description: The state-machine action to run.
                inventoryItemId:
                  type: string
                  description: For picking / verification actions.
                shippingRateObjectId:
                  type: string
                  description: A rate id from Shippo.
                shippingCode:
                  type: string
                overrideCarrierCode:
                  type: string
                  description: Override carrier (e.g. `ups`, `usps`, `dhl_express`, `sendle`).
                bigShippingLabelAddressType:
                  type: string
                  enum:
                    - Default
                    - CustomerReturn
                productType:
                  type: string
                  description: Product type — helps with slot matching.
                accessoryProductType:
                  type: string
                  description: Deprecated.
              additionalProperties: true
      responses:
        '200':
          description: Updated shipment.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BigShipment'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No shipment with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Action invalid for the shipment's current status, or required parameters missing.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:write
        - admin:all
    delete:
      tags:
        - Shipping
      operationId: deleteBigShipment
      summary: Delete a shipment
      description: Deletes a shipment. Stricter auth than the other shipping endpoints.
      responses:
        '200':
          description: Shipment deleted.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No shipment with this id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - FabricatorAdmin
      x-scopes:
        - shipping:write
        - admin:all
  /admin/shipper/shipment/{id}/shopify:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
    get:
      tags:
        - Shipping
      operationId: getBigShipmentShopifyInfo
      summary: Shopify info for a shipment
      description: Returns Shopify information related to the shipment, including fulfillment orders and fulfillment status.
      responses:
        '200':
          description: Shopify info payload.
          content:
            application/json:
              schema:
                type: object
                properties:
                  shopifyOrder:
                    type: object
                    description: Shape per Shopify Admin API.
                    additionalProperties: true
                  fulfillmentOrders:
                    type: array
                    items:
                      type: object
                      description: Shopify fulfillment-order record.
                      additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:read
        - admin:all
  /admin/shipper/shipment/{id}/checklist:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
    get:
      tags:
        - Shipping
      operationId: getBigShipmentChecklist
      summary: Run shipment checklist
      description: Runs the shipment checklist — validates the shipment's status and identifies any issues or next steps.
      responses:
        '200':
          description: Checklist payload.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    description: Current shipment status.
                  nextAction:
                    type: string
                    description: Suggested next state-machine action.
                  issues:
                    type: array
                    items:
                      type: object
                      properties:
                        severity:
                          type: string
                          description: e.g. `error`, `warning`, `info`.
                        message:
                          type: string
                        code:
                          type: string
                  inventoryStatus:
                    type: object
                    properties:
                      required:
                        type: integer
                      picked:
                        type: integer
                      verified:
                        type: integer
                      missing:
                        type: array
                        items:
                          type: string
                  shippingLabelStatus:
                    type: string
                  canShip:
                    type: boolean
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:read
        - admin:all
  /admin/shipper/shipment/{id}/customs-preview:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
    get:
      tags:
        - Shipping
      operationId: getCustomsPreview
      summary: Customs declaration preview
      description: Returns a customs declaration preview for an international shipment.
      responses:
        '200':
          description: Customs preview payload.
          content:
            application/json:
              schema:
                type: object
                properties:
                  shipmentId:
                    type: string
                  contentsType:
                    type: string
                    description: e.g. `merchandise`, `gift`, `documents`.
                  contentsExplanation:
                    type: string
                  eelPfc:
                    type: string
                    description: Electronic Export Information / FTR exemption code.
                  nonDeliveryOption:
                    type: string
                    description: '`return` or `abandon`.'
                  items:
                    type: array
                    items:
                      type: object
                      properties:
                        description:
                          type: string
                        quantity:
                          type: integer
                        valueAmount:
                          type: string
                        valueCurrency:
                          type: string
                        originCountry:
                          type: string
                        tariffNumber:
                          type: string
                        netWeight:
                          type: number
                        massUnit:
                          type: string
                  totalDeclaredValue:
                    type: string
                  currency:
                    type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - Inventory
      x-scopes:
        - shipping:read
        - admin:all
  /admin/shipper/sync:
    get:
      tags:
        - Shipping
      operationId: shipperSync
      summary: Sync shipments
      description: Triggers a shipment sync.
      responses:
        '200':
          description: Sync result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  shipmentsProcessed:
                    type: integer
                  shipmentsUpdated:
                    type: integer
                  durationMs:
                    type: integer
                  errors:
                    type: array
                    items:
                      type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - FabricatorReadOnly
      x-scopes:
        - shipping:read
        - admin:all
  /admin/shipper/cache_transactions:
    get:
      tags:
        - Shipping
      operationId: cacheShippoTransactions
      summary: Cache Shippo transactions
      description: Triggers caching of Shippo transactions.
      responses:
        '200':
          description: Cache result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  transactionsCached:
                    type: integer
                  pagesFetched:
                    type: integer
                  durationMs:
                    type: integer
                  errors:
                    type: array
                    items:
                      type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - FabricatorReadOnly
      x-scopes:
        - shipping:read
        - admin:all
  /admin/shippo/analytics:
    get:
      tags:
        - Shipping
      operationId: getShippoAnalytics
      summary: Shippo analytics summary
      description: Returns the most recently computed analytics summary (status breakdown, time-series, totals).
      responses:
        '200':
          description: Analytics summary payload (from the Redis-backed cache).
          content:
            application/json:
              schema:
                type: object
                properties:
                  generatedAt:
                    type: string
                    format: date-time
                  totalTransactions:
                    type: integer
                  totalSpend:
                    type: number
                  statusBreakdown:
                    type: object
                    additionalProperties:
                      type: integer
                  carrierBreakdown:
                    type: object
                    additionalProperties:
                      type: object
                      properties:
                        count:
                          type: integer
                        totalCost:
                          type: number
                  timeSeries:
                    type: array
                    items:
                      type: object
                      properties:
                        date:
                          type: string
                          format: date
                        count:
                          type: integer
                        cost:
                          type: number
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: '"No analytics cached yet. Trigger a refresh first."'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - FabricatorReadOnly
      x-scopes:
        - shipping:read
        - admin:all
  /admin/shippo/analytics/transactions:
    get:
      tags:
        - Shipping
      operationId: getShippoAnalyticsTransactions
      summary: Browse cached Shippo transactions
      description: |
        Returns the cached transaction rows behind the analytics summary. Returns an
        empty `transactions` array (not 404) when the cache is empty.
      parameters:
        - in: query
          name: status
          schema:
            type: string
          description: Filter by `tx.trackingStatus`.
        - in: query
          name: minDate
          schema:
            type: string
            format: date-time
        - in: query
          name: maxDate
          schema:
            type: string
            format: date-time
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Transactions page.
          content:
            application/json:
              schema:
                type: object
                required:
                  - transactions
                  - total
                  - limit
                  - offset
                properties:
                  transactions:
                    type: array
                    items:
                      type: object
                      description: Shippo transaction record (passthrough — see Shippo's spec).
                      properties:
                        _trackingNumber:
                          type: string
                          description: Redis hash key surfaced onto each row.
                        objectId:
                          type: string
                        status:
                          type: string
                        trackingNumber:
                          type: string
                        trackingStatus:
                          type: string
                        rate:
                          type: string
                        createdAt:
                          type: string
                          format: date-time
                        labelUrl:
                          type: string
                          format: uri
                        carrier:
                          type: string
                        servicelevel:
                          type: object
                          additionalProperties: true
                      additionalProperties: true
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - FabricatorReadOnly
      x-scopes:
        - shipping:read
        - admin:all
  /admin/shippo/analytics/cache-status:
    get:
      tags:
        - Shipping
      operationId: getShippoAnalyticsCacheStatus
      summary: Shippo analytics cache status
      description: Returns when the analytics cache was last built, how many transactions it covers, and how much TTL remains.
      responses:
        '200':
          description: Cache metadata.
          content:
            application/json:
              schema:
                type: object
                required:
                  - metadata
                  - ttlSeconds
                properties:
                  metadata:
                    type: object
                    properties:
                      totalCached:
                        type: integer
                      fetchedAt:
                        type: string
                        format: date-time
                      maxTransactions:
                        type: integer
                  ttlSeconds:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: '"No cache exists yet."'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - FabricatorReadOnly
      x-scopes:
        - shipping:read
        - admin:all
  /admin/shippo/analytics/refresh:
    post:
      tags:
        - Shipping
      operationId: refreshShippoAnalytics
      summary: Trigger Shippo analytics rebuild
      description: |
        Triggers the background worker that rebuilds the analytics cache: paginates
        through every Shippo transaction, writes them to Redis with a 1-hour TTL, and
        computes the summary. Fire-and-forget in production; synchronous under test.
        Stricter auth than the read-side endpoints.
      parameters:
        - in: query
          name: maxTransactions
          schema:
            type: integer
            default: 10000
          description: Max transactions to fetch in this run.
      responses:
        '200':
          description: '"OK" (plain text). The worker continues asynchronously in production — watch `/admin/workers/status` for completion.'
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - shipping:write
        - admin:all
  /admin/fabricator/job:
    post:
      tags:
        - Fabricator Jobs
      operationId: createFabricatorJob
      summary: Create a job from a verified scan request
      description: |
        Creates a new manufacturing job from a scan request that has reached
        `ReadyForFabricator`. Acquires a Redis distributed lock on the scan request,
        creates the Job DB record, and appends the new `jobId` to the scan request's
        `jobIds` array. Rarely called directly from Arda — usually the Big Order
        checklist creates jobs automatically.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - scanRequestId
              properties:
                scanRequestId:
                  type: string
                priority:
                  type: integer
                  description: Optional; clamped server-side to `[-1, 100]`.
      responses:
        '200':
          description: Created `Job`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Job'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Scan request not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank `scanRequestId`, or scan request is not in `ReadyForFabricator`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - fabricator:write
        - admin:all
  /admin/fabricator/job/{jobId}:
    parameters:
      - in: path
        name: jobId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Jobs
      operationId: getFabricatorJob
      summary: Fetch a job
      description: Returns the full `Job` record. Read-only passthrough; no filtering or anonymization.
      responses:
        '200':
          description: Job record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Job'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Job not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - fabricator:read
        - admin:all
    put:
      tags:
        - Fabricator Jobs
      operationId: updateFabricatorJob
      summary: Update a job and/or run a state-machine action
      description: |
        The primary mutating endpoint for jobs. Updates job fields and/or drives the
        state machine via an `action`. Multiple updates can be mixed into a single
        call (e.g. change priority *and* run an action).

        Supported `action` values include `TryScanVerification`,
        `TryExternalPreprocessorUpload`, `AddMessage` (requires `message`),
        `TryNFC` (requires `nfcTagId`), `TryToolPathCreation`,
        `ToolPathCreationSuccess`, `QASuccess` / `TryCreateInventoryItem`,
        `ResetToAwaitingScanVerification`, `ResetToAwaitingToolpath`,
        `ResetToAwaitingManufacture`, `DestroyJob`. Reset actions are blocked
        if the job already has live inventory.

        Priority is clamped to `[-1, 100]`. IPD updates set
        `ipdSource = BigOrderIpdSource.Admin`. Every update appends to `job.history`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                action:
                  type: string
                  description: State-machine action name.
                metaData:
                  description: Action-specific payload.
                priority:
                  type: integer
                  description: Clamped to `[-1, 100]`.
                name:
                  type: string
                ipd:
                  type: integer
                  description: Sets `ipdSource = Admin`.
                nfcTagId:
                  type: string
                  description: Required when `action = TryNFC`.
                message:
                  type: string
                  description: Required when `action = AddMessage`.
              additionalProperties: true
      responses:
        '200':
          description: Updated job (or action-specific shape — see markdown for the action).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Job'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Reset blocked because job has live inventory items, or caller lacks permission.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Job not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Invalid state transition for the action, or missing required field for the action.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
        - FabricatorAdmin
      x-scopes:
        - fabricator:write
        - admin:all
    delete:
      tags:
        - Fabricator Jobs
      operationId: deleteFabricatorJob
      summary: Hard-delete a job
      description: |
        Hard-deletes a job and its linked `WaxId` + `NfcTag` records. Stricter auth
        than `updateJob`. Destructive; no undo.
      responses:
        '200':
          description: '"OK".'
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Job not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - fabricator:write
        - admin:all
  /admin/fabricator/job/{jobId}/test_preprocessor:
    parameters:
      - in: path
        name: jobId
        required: true
        schema:
          type: string
        description: Path param is present but **ignored** — the handler always uses a hardcoded test scan.
    get:
      tags:
        - Fabricator Jobs
      operationId: testScanPreprocessor
      summary: Debug — run preprocessor against hardcoded test scan
      description: |
        Spawns the scan-preprocessor Python script against a hardcoded test scan.
        Returns immediately after the subprocess is spawned. Despite taking `:jobId`,
        the handler does not use it. Intended for verifying the preprocessor on a dev box.
      responses:
        '200':
          description: '"OK" (subprocess spawned; not yet finished).'
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Fabricator
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/job/{jobId}/extra:
    parameters:
      - in: path
        name: jobId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Jobs
      operationId: getJobExtraInfo
      summary: Auxiliary job info (bin id + linked inventory)
      description: |
        Returns auxiliary data not on the core `Job` record: the factory bin
        identifier (a hash of `jobId` via `HashUtils.factoryBinHash`) and any
        inventory items linked to the job. Safe to call repeatedly; no writes.
      responses:
        '200':
          description: Auxiliary info.
          content:
            application/json:
              schema:
                type: object
                required:
                  - binIdentifier
                  - inventoryItems
                properties:
                  binIdentifier:
                    type: string
                  inventoryItems:
                    type: object
                    required:
                      - items
                      - count
                    properties:
                      items:
                        type: array
                        items:
                          $ref: '#/components/schemas/BigInventoryItem'
                      count:
                        type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Job not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - FabricatorAdmin
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/jobs:
    get:
      tags:
        - Fabricator Jobs
      operationId: listFabricatorJobs
      summary: List jobs with filters and pagination
      description: |
        The primary query endpoint — used by Arda's fabricator grid and by Looper
        monitor scripts. Most filters are exact-match. `waxId` and `nfcTagId` are
        **shortcut queries** — when either is present the handler returns that one
        job and ignores all other filters.
      parameters:
        - in: query
          name: limit
          schema:
            type: integer
        - in: query
          name: offset
          schema:
            type: integer
        - in: query
          name: jobState
          schema:
            type: integer
          description: Exact `JobState` enum value.
        - in: query
          name: priority
          schema:
            type: integer
        - in: query
          name: minPriority
          schema:
            type: integer
        - in: query
          name: maxPriority
          schema:
            type: integer
        - in: query
          name: minCreatedAt
          schema:
            type: integer
            format: int64
        - in: query
          name: maxCreatedAt
          schema:
            type: integer
            format: int64
        - in: query
          name: ipd
          schema:
            type: integer
        - in: query
          name: minIpd
          schema:
            type: integer
        - in: query
          name: maxIpd
          schema:
            type: integer
        - in: query
          name: name
          schema:
            type: string
        - in: query
          name: bigOrderId
          schema:
            type: string
        - in: query
          name: waxId
          schema:
            type: string
          description: Shortcut — returns the single job with this WaxId.
        - in: query
          name: nfcTagId
          schema:
            type: string
          description: Shortcut — returns the single job with this NFC tag.
      responses:
        '200':
          description: Jobs page.
          content:
            application/json:
              schema:
                type: object
                required:
                  - items
                  - count
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/Job'
                  count:
                    type: integer
                  totalCount:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          description: Unknown query param, or invalid `jobState` parse.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
        - FabricatorReadOnly
      x-scopes:
        - fabricator:read
        - admin:all
    post:
      tags:
        - Fabricator Jobs
      operationId: getJobsWithQueryDeprecated
      summary: Body-based jobs query (deprecated)
      description: |
        Body-based query variant with cursor pagination. **Deprecated** — prefer
        `GET /admin/fabricator/jobs`. Kept around for legacy Looper scripts.
      deprecated: true
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                limit:
                  type: integer
                cursorId:
                  type: string
              additionalProperties: true
      responses:
        '200':
          description: Jobs page (same shape as the GET variant).
          content:
            application/json:
              schema:
                type: object
                required:
                  - items
                  - count
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/Job'
                  count:
                    type: integer
                  totalCount:
                    type: integer
                  nextCursorId:
                    type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          description: Empty/invalid body, or invalid filter values.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - fabricator:write
        - admin:all
  /admin/fabricator/job/{jobId}/scan:
    parameters:
      - in: path
        name: jobId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Jobs
      operationId: downloadScanFileFromJob
      summary: Download the customer's scan zip
      description: Downloads the raw scan zip uploaded by the customer's iOS face-scan app.
      responses:
        '200':
          description: Scan zip.
          content:
            application/zip:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Job or scan file not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - FabricatorAdmin
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/job/{jobId}/mesh:
    parameters:
      - in: path
        name: jobId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Jobs
      operationId: downloadMeshFromJob
      summary: Download the processed mesh
      description: Downloads the processed mesh file (output of the scan preprocessor).
      responses:
        '200':
          description: Mesh file.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          description: Job mesh not ready (job state earlier than `AwaitingToolPathCreation`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Job or mesh not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - FabricatorAdmin
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/job/{jobId}/debug_start_toolpath:
    parameters:
      - in: path
        name: jobId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Jobs
      operationId: startToolPathFromFusion
      summary: Production endpoint — start toolpath generation
      description: |
        Despite the `debug_` prefix, this is a **production** endpoint called by
        Fusion 360 workstations. Allocates a `WaxId`, creates a `ToolPath` record,
        advances the job state, and returns the compressed mesh data Fusion needs to
        generate G-code.
      responses:
        '200':
          description: Toolpath setup payload.
          content:
            application/json:
              schema:
                type: object
                required:
                  - id
                  - ipd
                  - meshData
                properties:
                  id:
                    type: string
                    description: New ToolPath id.
                  ipd:
                    type: integer
                    description: Job's IPD — Fusion needs this.
                  meshData:
                    type: string
                    description: Deflate-compressed, then base64-encoded mesh.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Job not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Job is not in a state that can start toolpath creation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - FabricatorAdmin
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/job/{jobId}/debug_upload_toolpath:
    parameters:
      - in: path
        name: jobId
        required: true
        schema:
          type: string
    put:
      tags:
        - Fabricator Jobs
      operationId: uploadToolPathForJob
      summary: Production endpoint — upload finished G-code
      description: |
        Despite the `debug_` prefix, this is a **production** endpoint called by
        Fusion 360 to upload the finished G-code file. Stores the file on S3,
        updates the ToolPath record, and runs `ToolPathCreationSuccess`.
        **Note:** if a job has multiple toolpaths, this endpoint only touches the
        first one (`toolPaths[0]`).
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
              properties:
                file:
                  type: string
                  format: binary
                  description: Completed `.nc` G-code file.
      responses:
        '200':
          description: '"OK".'
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Tool path or job not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - FabricatorAdmin
      x-scopes:
        - fabricator:write
        - admin:all
  /admin/fabricator/job/{jobId}/debug_revert_history:
    parameters:
      - in: path
        name: jobId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Jobs
      operationId: revertJobHistory
      summary: Undo the last history entry
      description: |
        Pops the last history entry on a job and reverses its state-machine effect.
        Use sparingly — revert semantics depend on what the previous action was.
      responses:
        '200':
          description: Updated `Job`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Job'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - FabricatorAdmin
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/job/{jobId}/toolpath:
    parameters:
      - in: path
        name: jobId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Jobs
      operationId: downloadToolPath
      summary: Download the WaxId-filtered G-code
      description: |
        Downloads the G-code toolpath file filtered by the job's `WaxId`. The
        G-code from Fusion contains 20 `(Bore<N>)` sections; only the bores whose
        bit is `1` in the WaxId are kept. Response is `text/plain` (attachment
        headers temporarily disabled). Uses the **most recent** toolpath snapshot.
      responses:
        '200':
          description: G-code (filtered).
          content:
            text/plain:
              schema:
                type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Job or toolpath not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - FabricatorAdmin
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/jobs/checklist:
    get:
      tags:
        - Fabricator Jobs
      operationId: runJobsChecklist
      summary: Run jobs checklist (not implemented)
      description: |
        **Not implemented.** The underlying `runJobsChecklist` is a stub; the
        endpoint always throws and surfaces as a 500. Kept registered so callers
        don't get a 404. Don't rely on it in new code.
      responses:
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          description: Always — handler is a stub.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - FabricatorAdmin
        - FabricatorReadOnly
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/jobs/report:
    get:
      tags:
        - Fabricator Jobs
      operationId: runJobsReport
      summary: Jobs dashboard (totals + per-state)
      description: |
        High-level jobs dashboard — totals plus a per-state breakdown. Posts a
        formatted summary to the `Fabricator` Discord channel as a side effect.
        Expensive — fetches every job from the DB; avoid calling in a loop.
      responses:
        '200':
          description: Report payload.
          content:
            application/json:
              schema:
                type: object
                required:
                  - totalJobs
                  - totalJobsInProgress
                  - totalJobsReadyToShip
                  - jobsByJobState
                properties:
                  totalJobs:
                    type: integer
                  totalJobsInProgress:
                    type: integer
                  totalJobsReadyToShip:
                    type: integer
                  jobsByJobState:
                    type: object
                    additionalProperties:
                      type: integer
                    description: Keyed by `JobState` enum **string name** (e.g. `AwaitingInit`).
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - FabricatorReadOnly
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/jobs/report2:
    get:
      tags:
        - Fabricator Jobs
      operationId: runProblemJobsReport
      summary: Problem-jobs dashboard
      description: |
        Returns jobs stuck in problem states beyond expected business-day
        thresholds. Used by Looper / monitor scripts to surface issues on Discord.
        `ManufacturingFailed` is flagged regardless of age.
      responses:
        '200':
          description: Problem-jobs report.
          content:
            application/json:
              schema:
                type: object
                required:
                  - jobsByProblemStates
                  - jobsWithoutInventory
                  - problemJobs
                properties:
                  jobsByProblemStates:
                    type: object
                    properties:
                      AwaitingToolPathCreation:
                        type: array
                        items:
                          type: string
                      AwaitingManufacture:
                        type: array
                        items:
                          type: string
                      ManufacturingFailed:
                        type: array
                        items:
                          type: string
                      AwaitingQA:
                        type: array
                        items:
                          type: string
                      AwaitingNFC:
                        type: array
                        items:
                          type: string
                      ScanVerificationFailed:
                        type: array
                        items:
                          type: string
                  jobsWithoutInventory:
                    type: array
                    items:
                      type: string
                    description: Jobs in `AwaitingFulfilment` with no inventory item.
                  problemJobs:
                    type: integer
                    description: Total count of flagged jobs.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - FabricatorReadOnly
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/product/{id}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
        description: Numeric Shopify product id (parsed as integer).
    get:
      tags:
        - Fabricator Jobs
      operationId: getFabricatorProduct
      summary: Fetch a Shopify product
      description: Read-only passthrough to Shopify Admin API for a product, scoped to the fabricator domain.
      responses:
        '200':
          description: Shopify product payload — see Shopify's spec for the full shape.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                    format: int64
                  title:
                    type: string
                  handle:
                    type: string
                  vendor:
                    type: string
                  product_type:
                    type: string
                  status:
                    type: string
                  tags:
                    type: string
                  variants:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                  created_at:
                    type: string
                    format: date-time
                  updated_at:
                    type: string
                    format: date-time
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          description: Blank or non-numeric `id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/fabricator/variant/{id}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
        description: Numeric Shopify variant id (parsed as integer).
    get:
      tags:
        - Fabricator Jobs
      operationId: getFabricatorVariant
      summary: Fetch a Shopify variant
      description: Read-only passthrough to Shopify Admin API for a product variant.
      responses:
        '200':
          description: Shopify variant payload — see Shopify's spec for the full shape.
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                    format: int64
                  product_id:
                    type: integer
                    format: int64
                  title:
                    type: string
                  sku:
                    type: string
                  price:
                    type: string
                  inventory_quantity:
                    type: integer
                  weight:
                    type: number
                  weight_unit:
                    type: string
                  barcode:
                    type: string
                  option1:
                    type: string
                  option2:
                    type: string
                  option3:
                    type: string
                  requires_shipping:
                    type: boolean
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          description: Blank or non-numeric `id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - fabricator:read
        - admin:all
  /admin/auth/schemas:
    get:
      tags:
        - Moderation
      operationId: getAuthSchemas
      summary: Auth schema enumerations
      description: Returns auth schema enumerations (e.g. access policies, scopes).
      responses:
        '200':
          description: |
            Schema enumeration payload — keys are the enum names (`AccessPolicy`,
            `BigscreenAccountStatus`, etc.); values are the legal members.
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  type: array
                  items:
                    type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
        - AccountsReadOnly
      x-scopes:
        - admin:all
  /admin/profile:
    get:
      tags:
        - Moderation
      operationId: getAdminProfile
      summary: Admin profile (OAuth-aware)
      description: |
        Returns the admin profile for the account specified in query params. Also
        accepts OAuth bearers carrying the `profile` scope.
      responses:
        '200':
          description: Sanitized admin account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdminAccount'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
        - AccountsReadOnly
      x-scopes:
        - profile
  /admin/account/query/{field}/{value}:
    parameters:
      - in: path
        name: field
        required: true
        schema:
          type: string
        description: Account field to search (e.g. `email`, `id`, `oculusId`).
      - in: path
        name: value
        required: true
        schema:
          type: string
    get:
      tags:
        - Moderation
      operationId: queryAccounts
      summary: Query accounts by field/value
      description: Returns accounts matching the supplied field/value pair.
      responses:
        '200':
          description: Matching accounts.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AdminAccount'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
        - AccountsReadOnly
      x-scopes:
        - accounts:read
        - admin:all
  /admin/search/{field}/{value}:
    parameters:
      - in: path
        name: field
        required: true
        schema:
          type: string
      - in: path
        name: value
        required: true
        schema:
          type: string
    get:
      tags:
        - Moderation
      operationId: searchDeviceHistory
      summary: Search device history
      description: Searches device history by field/value.
      responses:
        '200':
          description: Matching accounts (a.k.a. device-history hit list).
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AdminAccount'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
        - AccountsReadOnly
      x-scopes:
        - accounts:read
        - admin:all
  /admin/account/{bigscreenAccountId}:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    get:
      tags:
        - Moderation
      operationId: getAccountForAdmin
      summary: Detailed admin view of an account
      description: Returns the full admin view of a single account.
      responses:
        '200':
          description: Sanitized admin account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AdminAccount'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Account not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Moderator
        - AccountsReadOnly
      x-scopes:
        - accounts:read
        - admin:all
  /admin/account/{bigscreenAccountId}/ban:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    put:
      tags:
        - Moderation
      operationId: banAccount
      summary: Ban an account
      description: Bans an account. Cannot ban yourself or accounts with higher privileges.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - message
              properties:
                message:
                  type: string
                  description: Ban reason.
      responses:
        '200':
          description: Account banned.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Account not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Moderator
      x-scopes:
        - accounts:write
        - admin:all
  /admin/account/{bigscreenAccountId}/unban:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    put:
      tags:
        - Moderation
      operationId: unbanAccount
      summary: Unban an account
      responses:
        '200':
          description: Account unbanned.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Account not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Moderator
      x-scopes:
        - accounts:write
        - admin:all
  /admin/account/{bigscreenAccountId}/disable:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    put:
      tags:
        - Moderation
      operationId: disableAccount
      summary: Disable an account
      responses:
        '200':
          description: Account disabled.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Account not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Moderator
      x-scopes:
        - accounts:write
        - admin:all
  /admin/account/{bigscreenAccountId}/note:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    post:
      tags:
        - Moderation
      operationId: createAdminNote
      summary: Create an admin note on an account
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - text
              properties:
                text:
                  type: string
                  description: Note body.
                isPinned:
                  type: boolean
                  default: false
      responses:
        '200':
          description: Note created.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - accounts:write
        - admin:all
  /admin/account/{bigscreenAccountId}/email_update_ticket:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    get:
      tags:
        - Moderation
      operationId: getEmailUpdateTicket
      summary: Email update ticket
      description: Returns an email update ticket the account holder can use to confirm an email change.
      responses:
        '200':
          description: Ticket payload.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmailUpdateTicket'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - accounts:read
        - admin:all
  /admin/account/{bigscreenAccountId}/token:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    get:
      tags:
        - Moderation
      operationId: createLoginToken
      summary: Create a login token (v1)
      description: Creates a single-use login token that an admin can hand to the account holder.
      responses:
        '200':
          description: Token payload.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginToken'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - accounts:read
        - admin:all
  /admin/account/{bigscreenAccountId}/token2:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    get:
      tags:
        - Moderation
      operationId: createLoginTokenV2
      summary: Create a login token (v2)
      description: Newer-style login token; prefer this over v1 for new integrations.
      responses:
        '200':
          description: Token payload.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginToken2'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - accounts:read
        - admin:all
  /admin/account/{bigscreenAccountId}/device_history:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    get:
      tags:
        - Moderation
      operationId: getAccountDeviceHistory
      summary: Device history for an account
      responses:
        '200':
          description: Device-history payload.
          content:
            application/json:
              schema:
                type: object
                properties:
                  bigscreenAccountId:
                    type: string
                  devices:
                    type: array
                    items:
                      $ref: '#/components/schemas/DeviceStatus'
                  ips:
                    type: array
                    items:
                      $ref: '#/components/schemas/IPStatus'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - accounts:read
        - admin:all
  /admin/device/{deviceUniqueIdentifier}:
    parameters:
      - in: path
        name: deviceUniqueIdentifier
        required: true
        schema:
          type: string
    get:
      tags:
        - Moderation
      operationId: getDeviceStatus
      summary: Device status
      responses:
        '200':
          description: Device status record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeviceStatus'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - accounts:read
        - admin:all
    put:
      tags:
        - Moderation
      operationId: updateDeviceStatus
      summary: Update device status
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                status:
                  type: string
                  description: New status value (must match `AuthSchemas.AccessStatusCodes`).
                notes:
                  type: string
      responses:
        '200':
          description: Status updated.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - accounts:write
        - admin:all
  /admin/ip/{ip}:
    parameters:
      - in: path
        name: ip
        required: true
        schema:
          type: string
    get:
      tags:
        - Moderation
      operationId: getIPStatus
      summary: IP address status
      responses:
        '200':
          description: IP status record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IPStatus'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - accounts:read
        - admin:all
    put:
      tags:
        - Moderation
      operationId: updateIPStatus
      summary: Update IP status
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                status:
                  type: string
                  description: New status value (must match `AuthSchemas.AccessStatusCodes`).
                notes:
                  type: string
      responses:
        '200':
          description: Status updated.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - accounts:read
        - admin:all
  /admin/account/{bigscreenAccountId}/reports:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    get:
      tags:
        - Moderation
      operationId: getReports
      summary: Reports for an account
      description: Returns user-submitted reports involving the supplied account, broken down by direction.
      responses:
        '200':
          description: Reports payload.
          content:
            application/json:
              schema:
                type: object
                required:
                  - reportsAgainst
                  - reportsBy
                  - roomReportsAgainst
                properties:
                  reportsAgainst:
                    type: array
                    items:
                      $ref: '#/components/schemas/AccountReport'
                    description: Account reports filed against this account.
                  reportsBy:
                    type: array
                    items:
                      $ref: '#/components/schemas/AccountReport'
                    description: Account reports this account filed.
                  roomReportsAgainst:
                    type: array
                    items:
                      $ref: '#/components/schemas/RoomReport'
                    description: Room reports against rooms this account created.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - accounts:read
        - admin:all
  /admin/account_reports:
    get:
      tags:
        - Moderation
      operationId: getAllAccountReports
      summary: All account reports
      responses:
        '200':
          description: Array of account reports.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AccountReport'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - reports:read
        - admin:all
  /admin/room_reports:
    get:
      tags:
        - Moderation
      operationId: getAllRoomReports
      summary: All room reports
      responses:
        '200':
          description: Array of room reports.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/RoomReport'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - reports:read
        - admin:all
  /admin/account_report/{accountReportId}:
    parameters:
      - in: path
        name: accountReportId
        required: true
        schema:
          type: string
    get:
      tags:
        - Moderation
      operationId: getAccountReport
      summary: Fetch one account report
      responses:
        '200':
          description: Account report.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountReport'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Report not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Moderator
      x-scopes:
        - reports:read
        - admin:all
  /admin/room_report/{roomReportId}:
    parameters:
      - in: path
        name: roomReportId
        required: true
        schema:
          type: string
    get:
      tags:
        - Moderation
      operationId: getRoomReport
      summary: Fetch one room report
      responses:
        '200':
          description: Room report.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RoomReport'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Report not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Moderator
      x-scopes:
        - reports:read
        - admin:all
  /admin/account/{bigscreenAccountId}/history:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    get:
      tags:
        - Moderation
      operationId: getLoginHistory
      summary: Login history for an account
      responses:
        '200':
          description: Login history.
          content:
            application/json:
              schema:
                type: object
                properties:
                  bigscreenAccountId:
                    type: string
                  logins:
                    type: array
                    items:
                      type: object
                      properties:
                        timestamp:
                          type: string
                          format: date-time
                        ip:
                          type: string
                        deviceUniqueIdentifier:
                          type: string
                        userAgent:
                          type: string
                        authMethod:
                          type: string
                          description: e.g. `email-password`, `oculus`, `steam`.
                        success:
                          type: boolean
                      additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - accounts:read
        - admin:all
  /admin/account/{bigscreenAccountId}/event/{event}:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
      - in: path
        name: event
        required: true
        schema:
          type: string
    get:
      tags:
        - Moderation
      operationId: getAnalyticsEvent
      summary: Analytics event data for an account
      description: Returns analytics event data for the specified account/event combo.
      responses:
        '200':
          description: Event data.
          content:
            application/json:
              schema:
                type: object
                properties:
                  bigscreenAccountId:
                    type: string
                  event:
                    type: string
                  events:
                    type: array
                    items:
                      type: object
                      properties:
                        timestamp:
                          type: string
                          format: date-time
                        metaData:
                          type: object
                          additionalProperties: true
                      additionalProperties: true
                  count:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - accounts:read
        - admin:all
  /admin/account/{bigscreenAccountId}/stats:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    get:
      tags:
        - Moderation
      operationId: getAccountStats
      summary: Account statistics
      responses:
        '200':
          description: Stats payload.
          content:
            application/json:
              schema:
                type: object
                properties:
                  bigscreenAccountId:
                    type: string
                  totalLogins:
                    type: integer
                  totalSessions:
                    type: integer
                  totalRoomsCreated:
                    type: integer
                  totalReportsAgainst:
                    type: integer
                  totalReportsBy:
                    type: integer
                  totalEntitlements:
                    type: integer
                  firstSeenAt:
                    type: string
                    format: date-time
                  lastSeenAt:
                    type: string
                    format: date-time
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
        - AccountsReadOnly
      x-scopes:
        - accounts:read
        - admin:all
  /admin/account/{bigscreenAccountId}/logs:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    get:
      tags:
        - Moderation
      operationId: getAccountLogs
      summary: Log database entries for an account
      responses:
        '200':
          description: Log entries.
          content:
            application/json:
              schema:
                type: object
                properties:
                  bigscreenAccountId:
                    type: string
                  logs:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        timestamp:
                          type: string
                          format: date-time
                        level:
                          type: string
                          description: e.g. `info`, `warn`, `error`.
                        logType:
                          type: string
                        message:
                          type: string
                        details:
                          type: object
                          additionalProperties: true
                  count:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - accounts:read
        - admin:all
  /admin/logs/{logType}:
    parameters:
      - in: path
        name: logType
        required: true
        schema:
          type: string
        description: |
          Log type to filter on. The trailing segment is optional in the underlying route
          (`/admin/logs{/:logType}`) — when omitted, all log types are returned.
    get:
      tags:
        - Moderation
      operationId: getLogDatabaseQuery
      summary: Log database entries
      description: Returns log database entries, optionally filtered by log type.
      responses:
        '200':
          description: Log entries.
          content:
            application/json:
              schema:
                type: object
                properties:
                  logs:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        timestamp:
                          type: string
                          format: date-time
                        level:
                          type: string
                        logType:
                          type: string
                        bigscreenAccountId:
                          type: string
                        message:
                          type: string
                        details:
                          type: object
                          additionalProperties: true
                  count:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - admin:all
  /admin/alert:
    post:
      tags:
        - Moderation
      operationId: sendAlert
      summary: Send a Discord alert
      description: Posts a message to a Discord alert channel.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - source
                - message
              properties:
                source:
                  type: string
                message:
                  type: string
      responses:
        '200':
          description: Alert sent.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Moderator
      x-scopes:
        - admin:all
  /admin/shop/test_order:
    post:
      tags:
        - Fabricator Orders
      operationId: createTestOrder
      summary: Create a test Big Order
      description: |
        Creates a test Big Order (and a backing Shopify order with `origin = TestData`)
        from a small dummy payload. Used by integration tests and QA flows. Only works
        with test-origin orders — not a real-customer creation path.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - email
                - firstName
                - lastName
              properties:
                email:
                  type: string
                  format: email
                firstName:
                  type: string
                lastName:
                  type: string
                name:
                  type: string
                purpose:
                  type: string
                priority:
                  type: integer
                ipd:
                  type: integer
                  description: IPD for lens jobs.
      responses:
        '200':
          description: Created Big Order.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BigOrder'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          description: Validation error or "The job could not be created."
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:write
        - admin:all
  /admin/shop/shopify_order:
    post:
      tags:
        - Fabricator Orders
      operationId: createCustomShopifyOrder
      summary: Create a Shopify order via existing Big Order
      description: |
        Creates a custom Shopify order for an existing Bigscreen account by piggy-backing
        off one of that account's existing Big Orders. Used to make a follow-up order
        (e.g. a replacement lens) without re-entering customer details.

        Requires the customer to already exist in Bigscreen via an existing Big Order —
        cannot bootstrap a brand-new customer.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - originalBigOrderId
                - shopifyProducts
                - scanRequestAction
              properties:
                originalBigOrderId:
                  type: string
                shopifyProducts:
                  type: array
                  items:
                    type: object
                    required:
                      - productId
                      - variantId
                    properties:
                      productId:
                        type: string
                      variantId:
                        type: string
                scanRequestAction:
                  type: string
                  enum:
                    - DuplicateMostRecentScanRequest
                    - CreateNewScanRequest
                sendEmail:
                  type: boolean
                priority:
                  type: integer
                shopifyOrderMode:
                  type: string
                  enum:
                    - TestOrder
                    - Direct
                  description: Forced to `TestOrder` outside the main network.
                ipd:
                  type: integer
                  description: Triggers an `AdminOverrideIPD` action when present.
                convertToBigOrder:
                  type: boolean
      responses:
        '200':
          description: Created Shopify order, optional Big Order, plus non-fatal errors.
          content:
            application/json:
              schema:
                type: object
                properties:
                  shopifyOrder:
                    $ref: '#/components/schemas/ShopifyOrder'
                  bigOrder:
                    $ref: '#/components/schemas/BigOrder'
                  errors:
                    type: array
                    items:
                      type: object
                      properties:
                        message:
                          type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Big order, account, customer, product, or variant not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Validation error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:write
        - admin:all
  /admin/shop/shopify_order_2:
    post:
      tags:
        - Fabricator Orders
      operationId: createCustomShopifyOrder2
      summary: Create a Shopify order by Bigscreen account
      description: |
        Creates a custom Shopify order for a Bigscreen account by id, without requiring
        an existing Big Order to piggy-back off. Supports per-variant quantities and
        presentment currency. Prefer this over `/shopify_order` for new integrations.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - ownerBigscreenAccountId
                - shopifyVariants
                - scanRequestAction
              properties:
                ownerBigscreenAccountId:
                  type: string
                shopifyVariants:
                  type: array
                  items:
                    oneOf:
                      - type: string
                        description: Bare variant id.
                      - type: object
                        required:
                          - id
                        properties:
                          id:
                            type: string
                          quantity:
                            type: integer
                            minimum: 1
                scanRequestAction:
                  type: string
                  enum:
                    - DuplicateMostRecentScanRequest
                    - CreateNewScanRequest
                sendEmail:
                  type: boolean
                priority:
                  type: integer
                shopifyOrderMode:
                  type: string
                  enum:
                    - TestOrder
                    - Direct
                presentmentCurrency:
                  type: string
                  description: e.g. `USD`.
      responses:
        '200':
          description: Created Shopify order, optional Big Order, plus non-fatal errors.
          content:
            application/json:
              schema:
                type: object
                properties:
                  shopifyOrder:
                    $ref: '#/components/schemas/ShopifyOrder'
                  bigOrder:
                    $ref: '#/components/schemas/BigOrder'
                  errors:
                    type: array
                    items:
                      type: object
                      properties:
                        message:
                          type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Account or customer not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Validation error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:write
        - admin:all
  /admin/shop/shopify_orders:
    get:
      tags:
        - Fabricator Orders
      operationId: getShopifyOrders
      summary: List Shopify orders (paired with Big Orders)
      description: |
        Lists Shopify orders with search, status filtering, and cursor-based pagination.
        Each result is paired with its Big Order if one exists.

        **Note:** if `SHOPIFY_FACTORY_API_ACCESS_TOKEN` is empty (dev with no Shopify
        creds), returns generated dummy orders instead of hitting Shopify.
      parameters:
        - in: query
          name: limit
          schema:
            type: integer
            default: 25
        - in: query
          name: cursor
          schema:
            type: string
        - in: query
          name: search
          schema:
            type: string
          description: Search by order name.
        - in: query
          name: status
          schema:
            type: string
            enum:
              - Open
              - Closed
              - Cancelled
              - Archived
        - in: query
          name: fulfillment_status
          schema:
            type: string
            enum:
              - Fulfilled
              - Partial
              - Unfulfilled
              - Any
        - in: query
          name: cache
          schema:
            type: integer
          description: If `1`, return cached orders instead of hitting Shopify.
      responses:
        '200':
          description: Paginated Shopify orders + Big Orders.
          content:
            application/json:
              schema:
                type: object
                properties:
                  orders:
                    type: array
                    items:
                      type: object
                      properties:
                        shopifyOrder:
                          $ref: '#/components/schemas/ShopifyOrder'
                        bigOrder:
                          $ref: '#/components/schemas/BigOrder'
                  totalOrdersCount:
                    type: integer
                  prevCursor:
                    type: string
                  nextCursor:
                    type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          description: Invalid `status` / `fulfillment_status`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/shopify_order/{shopifyOrderId}:
    parameters:
      - in: path
        name: shopifyOrderId
        required: true
        schema:
          type: string
        description: Shopify's numeric order id.
    get:
      tags:
        - Fabricator Orders
      operationId: getShopifyOrder
      summary: Fetch a Shopify order
      description: Returns a single Shopify order. Proxies to the Shopify Admin API; no cache.
      responses:
        '200':
          description: Shopify order.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShopifyOrder'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Shopify order not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank `shopifyOrderId`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:read
        - admin:all
    delete:
      tags:
        - Fabricator Orders
      operationId: deleteShopifyTestOrder
      summary: Delete a test Shopify order
      description: Deletes a Shopify order. Only works for orders with `origin = TestData`. Throws on any non-test order.
      responses:
        '200':
          description: '"OK".'
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Order not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - orders:write
        - admin:all
  /admin/shop/shopify_order_cache/{shopifyOrderId}:
    parameters:
      - in: path
        name: shopifyOrderId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Orders
      operationId: getCachedShopifyOrder
      summary: Cached copy of a Shopify order
      description: |
        Reads the cached Shopify order, skipping the live Shopify API call. Stricter
        auth than the uncached variant. Use for debugging cache vs. live shape;
        otherwise prefer `GET /admin/shop/shopify_order/:shopifyOrderId`.
      responses:
        '200':
          description: Cached Shopify order (may be stale).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShopifyOrder'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Cached order not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/shopify_order/{shopifyOrderId}/fulfillments:
    parameters:
      - in: path
        name: shopifyOrderId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Orders
      operationId: getShopifyOrderFulfillments
      summary: Shopify fulfillments
      description: Returns Shopify fulfillments for an order — the concrete shipments Shopify has recorded.
      responses:
        '200':
          description: Array of Shopify fulfillment objects.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  description: Shopify fulfillment record — see Shopify's spec for the full shape.
                  properties:
                    id:
                      type: integer
                      format: int64
                    order_id:
                      type: integer
                      format: int64
                    status:
                      type: string
                      description: e.g. `success`, `cancelled`, `failure`.
                    tracking_company:
                      type: string
                    tracking_number:
                      type: string
                    tracking_url:
                      type: string
                      format: uri
                    created_at:
                      type: string
                      format: date-time
                    updated_at:
                      type: string
                      format: date-time
                    shipment_status:
                      type:
                        - string
                        - 'null'
                    line_items:
                      type: array
                      items:
                        type: object
                        additionalProperties: true
                  additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Fulfillments not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/shopify_order/{shopifyOrderId}/fulfillment_orders:
    parameters:
      - in: path
        name: shopifyOrderId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Orders
      operationId: getShopifyOrderFulfillmentOrders
      summary: Shopify fulfillment orders
      description: Returns Shopify fulfillment orders — the scheduling records that precede fulfillments.
      responses:
        '200':
          description: Array of Shopify fulfillment-order records.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  description: Shopify fulfillment-order record — see Shopify's spec for the full shape.
                  properties:
                    id:
                      type: integer
                      format: int64
                    order_id:
                      type: integer
                      format: int64
                    shop_id:
                      type: integer
                      format: int64
                    status:
                      type: string
                      description: e.g. `open`, `in_progress`, `closed`.
                    request_status:
                      type: string
                    supported_actions:
                      type: array
                      items:
                        type: string
                    line_items:
                      type: array
                      items:
                        type: object
                        additionalProperties: true
                    destination:
                      type: object
                      additionalProperties: true
                  additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Order not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/shopify_order/{shopifyOrderId}/line_item:
    parameters:
      - in: path
        name: shopifyOrderId
        required: true
        schema:
          type: string
    post:
      tags:
        - Fabricator Orders
      operationId: createShopifyOrderLineItem
      summary: Add a line item to a Shopify order
      description: |
        Adds a line item to an existing Shopify order. **Note:** the current handler
        has known issues — fields appear to be read but ignored in favor of hardcoded
        values. Verify in code before relying on this endpoint.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - shopifyProductVariantId
                - quantity
              properties:
                shopifyProductVariantId:
                  oneOf:
                    - type: string
                    - type: number
                quantity:
                  type: integer
      responses:
        '200':
          description: Updated Shopify order.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShopifyOrder'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Order or variant not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - orders:write
        - admin:all
  /admin/shop/shopify_order/{shopifyOrderId}/line_item/{shopifyLineItemId}:
    parameters:
      - in: path
        name: shopifyOrderId
        required: true
        schema:
          type: string
      - in: path
        name: shopifyLineItemId
        required: true
        schema:
          type: string
    delete:
      tags:
        - Fabricator Orders
      operationId: removeShopifyOrderLineItem
      summary: Remove a line item from a Shopify order
      description: Removes a line item from a Shopify order. Same caveat as the POST variant — handler may use hardcoded ids.
      responses:
        '200':
          description: Updated Shopify order.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShopifyOrder'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Order or line item not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - orders:write
        - admin:all
  /admin/shop/shopify_order/{shopifyOrderId}/risks:
    parameters:
      - in: path
        name: shopifyOrderId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Orders
      operationId: getShopifyOrderRisks
      summary: Shopify fraud-risk assessments
      description: Returns Shopify fraud-risk assessments for an order.
      responses:
        '200':
          description: Array of Shopify order-risk records.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  description: Shopify order-risk record — see Shopify's spec for the full shape.
                  properties:
                    id:
                      type: integer
                      format: int64
                    order_id:
                      type: integer
                      format: int64
                    cause_cancel:
                      type: boolean
                    display:
                      type: boolean
                    message:
                      type: string
                    recommendation:
                      type: string
                      description: '`accept`, `investigate`, or `cancel`.'
                    score:
                      type: string
                      description: Numeric string between 0 and 1.
                    source:
                      type: string
                      description: e.g. `Shopify`, `External`.
                  additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Order not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/webhooks/install:
    post:
      tags:
        - Fabricator Orders
      operationId: installShopifyWebhooks
      summary: Install Shopify webhooks
      description: Installs the webhooks admin_api expects Shopify to call (order created, updated, paid, fulfilled, etc.).
      responses:
        '200':
          description: Install summary.
          content:
            application/json:
              schema:
                type: object
                properties:
                  installed:
                    type: array
                    items:
                      type: string
                    description: Topics installed (e.g. `orders/create`, `orders/paid`).
                  skipped:
                    type: array
                    items:
                      type: string
                  errors:
                    type: array
                    items:
                      type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - orders:write
        - admin:all
  /admin/shop/webhooks/uninstall:
    post:
      tags:
        - Fabricator Orders
      operationId: uninstallShopifyWebhooks
      summary: Uninstall Shopify webhooks
      description: Removes all webhooks admin_api has registered with Shopify.
      responses:
        '200':
          description: Uninstall summary.
          content:
            application/json:
              schema:
                type: object
                properties:
                  removed:
                    type: array
                    items:
                      type: string
                  errors:
                    type: array
                    items:
                      type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
      x-scopes:
        - orders:write
        - admin:all
  /admin/shop/webhooks:
    get:
      tags:
        - Fabricator Orders
      operationId: getShopifyWebhookResults
      summary: List registered Shopify webhooks
      description: Returns the webhooks Shopify has recorded as registered for this app.
      responses:
        '200':
          description: Array of Shopify webhook records.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: integer
                      format: int64
                    topic:
                      type: string
                      description: e.g. `orders/create`, `orders/paid`.
                    address:
                      type: string
                      format: uri
                    format:
                      type: string
                      example: json
                    api_version:
                      type: string
                    created_at:
                      type: string
                      format: date-time
                    updated_at:
                      type: string
                      format: date-time
                    fields:
                      type: array
                      items:
                        type: string
                    metafield_namespaces:
                      type: array
                      items:
                        type: string
                  additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - FabricatorAdmin
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/shopify_orders/pair:
    post:
      tags:
        - Fabricator Orders
      operationId: createBeyondLensPair
      summary: Create a Beyond + lens-insert order pair
      description: |
        Creates a pair linking a Beyond-only Shopify order to a separate Lens-insert-only
        order for the same customer. Fabricator pipeline then treats the two as a
        single shipment. Search is by **order name**, not id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - beyondShopifyOrderName
                - lensOnlyShopifyOrderName
              properties:
                beyondShopifyOrderName:
                  type: string
                lensOnlyShopifyOrderName:
                  type: string
      responses:
        '200':
          description: Created pair record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BeyondLensOrderPair'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Beyond or lens order not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank names, customer mismatch, or invalid line-item composition on either side.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - FabricatorAdmin
      x-scopes:
        - orders:write
        - admin:all
    get:
      tags:
        - Fabricator Orders
      operationId: findBeyondLensPair
      summary: Find a pair by Beyond order name
      parameters:
        - in: query
          name: beyondShopifyOrderName
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Pair record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BeyondLensOrderPair'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Beyond order not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank `beyondShopifyOrderName`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - FabricatorAdmin
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/shopify_orders/pairs:
    get:
      tags:
        - Fabricator Orders
      operationId: getAllBeyondLensPairs
      summary: List all order pairs
      responses:
        '200':
          description: Array of pair records.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/BeyondLensOrderPair'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - FabricatorAdmin
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/shopify_orders/pair/{pairId}:
    parameters:
      - in: path
        name: pairId
        required: true
        schema:
          type: string
    delete:
      tags:
        - Fabricator Orders
      operationId: deleteBeyondLensPair
      summary: Remove an order pair
      description: Removes a pair record. Does not delete the underlying Shopify orders.
      responses:
        '200':
          description: '"OK".'
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Pair not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank `pairId`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - FabricatorAdmin
      x-scopes:
        - orders:write
        - admin:all
  /admin/shop/orders:
    get:
      tags:
        - Fabricator Orders
      operationId: getBigOrders
      summary: List Big Orders
      description: Lists Big Orders with filtering and pagination.
      parameters:
        - in: query
          name: offset
          schema:
            type: integer
        - in: query
          name: limit
          schema:
            type: integer
        - in: query
          name: nextAction
          schema:
            type: string
          description: Filter by the checklist's next-action value.
        - in: query
          name: bigscreenAccountId
          schema:
            type: string
        - in: query
          name: shopifyOrderId
          schema:
            type: string
        - in: query
          name: exactShopifyProductIds
          schema:
            type: string
          description: CSV — orders that contain all of these product ids.
        - in: query
          name: exactShopifyVariantIds
          schema:
            type: string
          description: CSV — orders that contain all of these variant ids.
      responses:
        '200':
          description: Big Orders page.
          content:
            application/json:
              schema:
                type: object
                required:
                  - items
                  - count
                  - totalCount
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/BigOrder'
                  count:
                    type: integer
                  totalCount:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          description: Query-param validation error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/order:
    post:
      tags:
        - Fabricator Orders
      operationId: createBigOrder
      summary: Create a Big Order from a Shopify order
      description: |
        Idempotent by default — if a Big Order already exists for the Shopify order,
        returns it unchanged. With `audiostrap=1`, permits creating a second Big
        Order for the same Shopify order to handle a separate audio-strap line item.
      parameters:
        - in: query
          name: audiostrap
          schema:
            type: string
            enum:
              - '1'
          description: When set, allow a second Big Order for an audio-strap add-on.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - shopifyOrderId
              properties:
                shopifyOrderId:
                  oneOf:
                    - type: string
                    - type: number
                scanRequestAction:
                  type: string
                  enum:
                    - DuplicateMostRecentScanRequest
                    - CreateNewScanRequest
      responses:
        '200':
          description: Created (or existing) Big Order.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BigOrder'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          description: Validation, fulfillability, or duplicate-audio-strap error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:write
        - admin:all
  /admin/shop/order/{bigOrderId}:
    parameters:
      - in: path
        name: bigOrderId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Orders
      operationId: getBigOrder
      summary: Fetch a Big Order
      responses:
        '200':
          description: Big Order.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BigOrder'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Big Order not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:read
        - admin:all
    put:
      tags:
        - Fabricator Orders
      operationId: updateBigOrderAdmin
      summary: Run a Big Order action
      description: |
        Runs a Big Order action — the write side of the checklist state machine.
        Acquires a distributed lock on the Big Order for the action's duration.
        Response shape varies by action — typically a `ScanRequest`, `Job`,
        `ScanRequestEmailResponse`, or the updated `BigOrder`. Dispatch on `action`
        client-side to know which shape to expect. `silentEmail=true` suppresses
        customer-facing email for actions that would normally send one.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - action
              properties:
                action:
                  type: string
                  description: A `BigOrderAction` enum value.
                priority:
                  type: integer
                ipd:
                  type: integer
                  minimum: 55
                  maximum: 75
                scanRequestId:
                  type: string
                overrideJobId:
                  type: string
                shipmentHold:
                  type: boolean
                shippingServiceLevel:
                  type: string
                silentEmail:
                  type: boolean
      responses:
        '200':
          description: |
            Action result. Shape varies by `action`:
            - `SendScanRequest` / scan-related actions → a `ScanRequest`.
            - `CreateJob` → a `Job`.
            - email-sending actions → `{ scanRequest, emailResponse }`.
            - everything else → the updated `BigOrder`.

            The default schema below is `BigOrder`; cast the response in your client based on the `action` you sent.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/BigOrder'
                  - $ref: '#/components/schemas/ScanRequest'
                  - $ref: '#/components/schemas/Job'
                  - type: object
                    description: Email-action shape.
                    properties:
                      scanRequest:
                        $ref: '#/components/schemas/ScanRequest'
                      emailResponse:
                        type: object
                        additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Big Order not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Validation error or invalid action for current state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:write
        - admin:all
    delete:
      tags:
        - Fabricator Orders
      operationId: deleteBigOrder
      summary: Hard-delete a Big Order
      description: Hard-deletes a Big Order. No soft-delete or recovery. Does not delete the underlying Shopify order.
      responses:
        '200':
          description: '"OK".'
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Big Order not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Admin
        - FabricatorAdmin
      x-scopes:
        - orders:write
        - admin:all
  /admin/shop/order/{bigOrderId}/scan_requests:
    parameters:
      - in: path
        name: bigOrderId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Orders
      operationId: getBigOrderScanRequests
      summary: Scan requests for a Big Order
      description: Lists scan requests associated with a Big Order's account + order combo.
      responses:
        '200':
          description: Scan requests page.
          content:
            application/json:
              schema:
                type: object
                required:
                  - items
                  - count
                  - totalCount
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/ScanRequest'
                  count:
                    type: integer
                  totalCount:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Big Order not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/order/{bigOrderId}/shipping_summary:
    parameters:
      - in: path
        name: bigOrderId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Orders
      operationId: getBigOrderShippingSummary
      summary: Shipping summary for a Big Order
      description: |
        Computes the shipping summary — line-item-to-shipment-group mapping, current
        shipment per group, and available inventory slots for groups that haven't
        shipped yet. This is what Arda renders on the Big Order detail page.
      responses:
        '200':
          description: Shipping summary.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BigOrderShippingSummary'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Big Order or its Shopify order not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/order/{bigOrderId}/shipments:
    parameters:
      - in: path
        name: bigOrderId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Orders
      operationId: getBigOrderShipments
      summary: Shipments for a Big Order
      description: Lists every shipment created for a Big Order, across all shipment groups.
      responses:
        '200':
          description: Shipments page.
          content:
            application/json:
              schema:
                type: object
                required:
                  - items
                  - count
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/BigShipment'
                  count:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Big Order or shipment not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/account/{bigscreenAccountId}/orders:
    parameters:
      - in: path
        name: bigscreenAccountId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Orders
      operationId: getBigOrdersForAccount
      summary: Big Orders for an account
      description: Lists every Big Order belonging to the supplied Bigscreen account.
      responses:
        '200':
          description: Big Orders page.
          content:
            application/json:
              schema:
                type: object
                required:
                  - items
                  - count
                  - totalCount
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/BigOrder'
                  count:
                    type: integer
                  totalCount:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Account not found (the message is "The big order could not be found.", a known misleading text).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Blank id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/order/{bigOrderId}/checklist:
    parameters:
      - in: path
        name: bigOrderId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Orders
      operationId: runBigOrderChecklistOnce
      summary: Run the checklist (read-only)
      description: |
        Runs the Big Order checklist to compute the next action without mutating the
        order. Idempotent but does real work (Shopify lookups, inventory queries).
        **Note:** a 422 with `success: false` is not an API failure — it means the
        checklist ran fine but the order isn't ready to advance.
      responses:
        '200':
          description: Checklist result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BigOrderChecklistResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Big Order not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: 'Skipped (already shipped/cancelled), missing account, or `success: false` checklist result.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BigOrderChecklistResult'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/order/{bigOrderId}/sync_line_items:
    parameters:
      - in: path
        name: bigOrderId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Orders
      operationId: syncBigOrderLineItems
      summary: Re-read line items from Shopify
      description: Re-reads line items from Shopify and persists any changes to the Big Order.
      responses:
        '200':
          description: Sync result — diff summary of what changed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BigOrderSyncResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/order/{bigOrderId}/sync:
    parameters:
      - in: path
        name: bigOrderId
        required: true
        schema:
          type: string
    get:
      tags:
        - Fabricator Orders
      operationId: syncBigOrder
      summary: Full sync of a single Big Order
      description: |
        Pulls fresh order data from Shopify, updates line items, refreshes line-item
        types and snapshots, repairs shipment groups if needed, and updates snapshots
        for Arda-auto-added items. Uses a Redis distributed lock to prevent concurrent
        syncs for the same order. Will not sync shipped or cancelled orders.
      responses:
        '200':
          description: Change summary, optionally including the updated `BigOrder`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BigOrderSyncResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Big Order or Shopify order not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Skipped (already shipped/cancelled) or blank id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/orders/report:
    get:
      tags:
        - Fabricator Orders
      operationId: runBigOrdersReport
      summary: Big Orders report
      description: |
        Runs the Big Orders report — totals plus per-state breakdown across
        every Big Order. Posts a Discord summary as a side effect; returns the
        same payload to the caller.
      responses:
        '200':
          description: Report data.
          content:
            application/json:
              schema:
                type: object
                properties:
                  totalBigOrders:
                    type: integer
                  totalInProgress:
                    type: integer
                  totalReadyToShip:
                    type: integer
                  totalShipped:
                    type: integer
                  bigOrdersByState:
                    type: object
                    additionalProperties:
                      type: integer
                    description: Keyed by `BigOrderState` enum string name (e.g. `AwaitingScanRequest`).
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Fabricator
        - FabricatorReadOnly
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/orders/report2:
    get:
      tags:
        - Fabricator Orders
      operationId: runBigOrdersReport2
      summary: Big Orders report (v2)
      description: V2 of the Big Orders report. Different shape than v1; pick whichever matches the UI you're building.
      responses:
        '200':
          description: Report data (v2 shape).
          content:
            application/json:
              schema:
                type: object
                properties:
                  generatedAt:
                    type: string
                    format: date-time
                  totalBigOrders:
                    type: integer
                  problemBigOrders:
                    type: array
                    items:
                      type: object
                      properties:
                        bigOrderId:
                          type: string
                        shopifyOrderName:
                          type: string
                        state:
                          type: string
                        stuckSinceDays:
                          type: integer
                  stateBreakdown:
                    type: object
                    additionalProperties:
                      type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Fabricator
        - FabricatorReadOnly
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/orders/checklist:
    get:
      tags:
        - Fabricator Orders
      operationId: runBigOrderChecklistAll
      summary: Run checklist over all Big Orders
      description: |
        Runs the checklist worker over every Big Order. Async in production
        (returns `"OK"` immediately); synchronous in test envs. Watch
        `/admin/workers/status` for completion.
      responses:
        '200':
          description: '"OK" — fire-and-forget in production.'
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Fabricator
        - FabricatorReadOnly
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/orders/data:
    get:
      tags:
        - Fabricator Orders
      operationId: getBigOrdersData
      summary: Big Orders data export
      responses:
        '200':
          description: Bulk Big Orders data export.
          content:
            application/json:
              schema:
                type: object
                properties:
                  generatedAt:
                    type: string
                    format: date-time
                  totalBigOrders:
                    type: integer
                  bigOrders:
                    type: array
                    items:
                      $ref: '#/components/schemas/BigOrder'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Fabricator
        - FabricatorReadOnly
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/orders/sync:
    get:
      tags:
        - Fabricator Orders
      operationId: syncAllBigOrders
      summary: Async sync of all Big Orders (worker)
      description: Enqueues an async `orders_sync` worker. Fire-and-forget; observe via `/admin/workers/status`.
      responses:
        '200':
          description: '"OK".'
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - FabricatorReadOnly
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/orders/sync2:
    get:
      tags:
        - Fabricator Orders
      operationId: syncBigOrders2
      summary: Alternate Big Orders sync
      description: Alternate sync path. Also used by the Shopify webhook handler.
      responses:
        '200':
          description: Aggregate sync summary.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ordersProcessed:
                    type: integer
                  ordersChanged:
                    type: integer
                  durationMs:
                    type: integer
                  errors:
                    type: array
                    items:
                      type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - FabricatorReadOnly
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/orders/sync3:
    get:
      tags:
        - Fabricator Orders
      operationId: syncBigOrders3
      summary: Big Orders sync (v3)
      description: Third sync variant — historical separation from sync2. Prefer sync3 for new work unless you have a reason to pin to an older path.
      responses:
        '200':
          description: Aggregate sync summary (v3 shape).
          content:
            application/json:
              schema:
                type: object
                properties:
                  ordersProcessed:
                    type: integer
                  ordersChanged:
                    type: integer
                  durationMs:
                    type: integer
                  errors:
                    type: array
                    items:
                      type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - FabricatorReadOnly
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/orders/sync_all_big_orders:
    get:
      tags:
        - Fabricator Orders
      operationId: syncAllBigOrdersAndRepair
      summary: Resync all Big Orders, repair shipment groups
      description: |
        Resyncs all Big Orders and repairs shipment groups. Async in production,
        synchronous in test envs.
      responses:
        '200':
          description: '"OK".'
          content:
            text/plain:
              schema:
                type: string
                example: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Admin
        - FabricatorReadOnly
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/sync/{shopifyOrderId}:
    parameters:
      - in: path
        name: shopifyOrderId
        required: true
        schema:
          type: string
        description: |
          The trailing segment is **optional** in the registered route
          (`/admin/shop/sync{/:shopifyOrderId}`) — but the handler is a stub either way.
    get:
      tags:
        - Fabricator Orders
      operationId: syncShopifyOrderPlaceholder
      summary: Placeholder (not implemented)
      description: |
        Placeholder for a future "sync a specific Shopify order into its Big Order"
        endpoint. Currently always returns `400 "Not implemented."` — calling this
        does nothing useful today. Use `GET /admin/shop/order/{bigOrderId}/sync`
        for per-order sync instead.
      responses:
        '400':
          description: '"Not implemented." (plain text).'
          content:
            text/plain:
              schema:
                type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/products:
    get:
      tags:
        - Fabricator Orders
      operationId: getShopifyProducts
      summary: List Shopify products
      description: Lists Shopify products directly from Shopify (not cached).
      parameters:
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Array of Shopify products.
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  description: Shopify product — see Shopify's spec for the full shape.
                  properties:
                    id:
                      type: integer
                      format: int64
                    title:
                      type: string
                    handle:
                      type: string
                    body_html:
                      type: string
                    vendor:
                      type: string
                    product_type:
                      type: string
                    status:
                      type: string
                      description: '`active`, `archived`, `draft`.'
                    tags:
                      type: string
                    variants:
                      type: array
                      items:
                        type: object
                        additionalProperties: true
                    images:
                      type: array
                      items:
                        type: object
                        additionalProperties: true
                    created_at:
                      type: string
                      format: date-time
                    updated_at:
                      type: string
                      format: date-time
                    published_at:
                      type: string
                      format: date-time
                  additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          description: Query validation error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/products_snapshot:
    get:
      tags:
        - Fabricator Orders
      operationId: getShopifyProductsSnapshot
      summary: In-memory Shopify product snapshot
      description: |
        Returns the in-memory snapshot of Shopify products (variant map). Fast — no API
        call — but potentially stale. Refreshed at process start and on certain sync events.
      responses:
        '200':
          description: |
            `ShopifyData.ShopifyProductVariantMap` — keyed by Shopify variant id, value
            is the variant payload plus a denormalized parent product reference.
          content:
            application/json:
              schema:
                type: object
                description: Variant id → variant record map.
                additionalProperties:
                  type: object
                  properties:
                    id:
                      type: integer
                      format: int64
                    product_id:
                      type: integer
                      format: int64
                    title:
                      type: string
                    sku:
                      type: string
                    price:
                      type: string
                    inventory_quantity:
                      type: integer
                    weight:
                      type: number
                    weight_unit:
                      type: string
                    barcode:
                      type: string
                    option1:
                      type: string
                    option2:
                      type: string
                    option3:
                      type: string
                    requires_shipping:
                      type: boolean
                  additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:read
        - admin:all
  /admin/shop/test/getBeyondPackageInventoryCode:
    post:
      tags:
        - Fabricator Orders
      operationId: testGetBeyondPackageInventoryCode
      summary: Compute Beyond package inventory code
      description: |
        Computes the Beyond package inventory code for a given product-type combo.
        Thin wrapper around `ShippingAdminApi.getBeyondPackageInventoryCode`; exposed
        for QA and factory-tool debugging.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - beyondProductType
                - shellProductType
              properties:
                beyondProductType:
                  type: string
                shellProductType:
                  type: string
                cushionProductType:
                  type: string
                hasAccessories:
                  type: boolean
                hasLenses:
                  type: boolean
      responses:
        '200':
          description: Inventory-code computation result.
          content:
            application/json:
              schema:
                type: object
                properties:
                  inventoryCode:
                    type: string
                    description: Computed package inventory code.
                  packageType:
                    type: string
                    description: Resolved `BigShipmentPackageType`.
                  weight:
                    type: number
                  dimensions:
                    type: object
                    properties:
                      length:
                        type: number
                      width:
                        type: number
                      height:
                        type: number
                  breakdown:
                    type: object
                    additionalProperties: true
                    description: Per-product-type contribution to the final code.
        '400':
          description: '"beyondProductType and shellProductType are required".'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
      x-access-policies:
        - Fabricator
      x-scopes:
        - orders:write
        - admin:all
components:
  securitySchemes:
    adminApiKey:
      type: http
      scheme: bearer
      bearerFormat: AdminApiKey-or-OAuthJWT
      description: |
        64-character app-key registered for the `admin-api` application, OR an OAuth 2.0
        bearer JWT minted by the Bigscreen OAuth provider. The bearer shape (`xxx.yyy.zzz`
        vs single 64-char string) determines which admission path runs.
    adminAccessToken:
      type: apiKey
      in: header
      name: x-access-token
      description: |
        JWT for the **end-user account** the request is acting on behalf of. Required
        in addition to `Authorization` for `/admin/*`. Contains the access policies
        (`Admin`, `Inventory`, etc.) that gate per-endpoint authorization.
    fusionApiKey:
      type: http
      scheme: bearer
      description: |
        Used by `/fusion/*` only — Fusion 360 factory workstations. No access token
        required (network-level gating via load balancer).
    cncMachineApiKey:
      type: http
      scheme: bearer
      description: Used by `/cnc/*` together with a `CncMachine` access token.
    cncAccessToken:
      type: apiKey
      in: header
      name: x-access-token
      description: Operator JWT for `/cnc/*`.
    poseApiKey:
      type: http
      scheme: bearer
      description: Used by `/pose/*` together with a `PoseAppUser` access token.
    poseAccessToken:
      type: apiKey
      in: header
      name: x-access-token
      description: Operator JWT for `/pose/*`.
  responses:
    Unauthorized:
      description: Missing or invalid `Authorization` / `x-access-token`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: Authenticated but lacks the required access policy or scope.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: Human-readable error message.
        details:
          description: Optional structured details (validation errors, etc.).
      example:
        error: Forbidden
        details:
          requiredPolicy: Admin
    BigInventoryItemStatus:
      type: string
      description: |
        State-machine position of an inventory item. Drives shipping eligibility,
        report inclusion, and dashboard counts.
      enum:
        - Unknown
        - RequiresQA
        - InStock
        - InShippingQueue
        - WithShipment
        - Shipped
        - Damaged
        - Deleted
      x-enum-descriptions:
        Unknown: Default — should not appear on persisted rows.
        RequiresQA: Item needs a QA step before it can be stocked.
        InStock: Available in inventory; ready to assign to a shipment.
        InShippingQueue: Selected for shipment; not yet packed.
        WithShipment: Attached to a `BigShipment`; not yet shipped.
        Shipped: Handed off to the carrier.
        Damaged: Flagged as damaged; not eligible for shipping.
        Deleted: Soft-deleted; hidden from default queries.
    BigInventoryTrackingType:
      type: string
      description: |
        How quantity is tracked for this item. `Personalized` items always have
        `quantity = 1` and a unique `serialNumber` (one customer, one cushion).
        `QuantityTracked` items aggregate (e.g. a tray of 200 soft straps).
        `Untracked` items are not counted (cables, screws — anything where exact
        inventory count isn't worth maintaining).
      enum:
        - Unknown
        - Personalized
        - QuantityTracked
        - Untracked
    BigProductType:
      type: string
      description: |
        Product taxonomy. Maps to Shopify variants for sellable items and to internal
        fabricator data for manufactured cushions. Adding a value requires a
        corresponding metafield in Shopify (see internal docs).
      enum:
        - Unknown
        - BigscreenBeyondV1
        - BeyondCushionV1
        - AudioStrapV1
        - BeyondFibreOpticCableV1
        - BeyondLinkBoxV1
        - BeyondSoftStrapV1
        - ReplacementBigscreenBeyondV1
        - ReplacementBeyondCushionV1
        - ReplacementBeyondFibreOpticCableV1
        - ReplacementBeyondLinkBoxV1
        - ReplacementBeyondSoftStrapV1
        - BeyondCyberboxV1
        - IPDDiscoveryUnitV1
        - CustomCushion
        - PrescriptionLenses
        - PrescriptionLensInserts
        - AudioStrap
        - Beyond2
        - Beyond2Upgrade
        - Beyond2Eye
        - Beyond2EyeUpgrade
        - Beyond2EyeVRChat
        - UniversalHalo
        - CoverShell
        - GenericCushion
        - Merchandise
        - Beyond2IPDTool
        - BigscreenRacingGloves
        - LinkBooster
        - Other
    BigInventoryItem:
      type: object
      description: |
        One physical (or virtual) inventory unit. Mirrors the `big_inventory_items`
        table; created by the manufacturing pipeline and consumed by the shipping
        pipeline.
      required:
        - id
        - type
        - status
        - trackingType
      properties:
        id:
          type: string
        creatorBigscreenAccountId:
          type: string
        serialNumber:
          type: string
          description: Unique for `Personalized`; batch id or empty for the others.
        sku:
          type: string
        shopifySku:
          type: string
        type:
          $ref: '#/components/schemas/BigProductType'
        trackingType:
          $ref: '#/components/schemas/BigInventoryTrackingType'
        quantity:
          type: integer
          minimum: 0
          default: 0
        status:
          $ref: '#/components/schemas/BigInventoryItemStatus'
        history:
          type: array
          items:
            type: object
            additionalProperties: true
        bigShipmentId:
          type: string
          description: Set when `status` becomes `WithShipment`.
        pickingHint:
          description: Free-form payload set by the picking station.
        jobId:
          type: string
          description: Manufacturing job that produced this item, if any.
        forecastBigOrderId:
          type: string
          description: For pre-allocated items reserved against a future order.
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    BigInventoryItemCreationParams:
      type: object
      properties:
        serialNumber:
          type: string
        sku:
          type: string
        productType:
          $ref: '#/components/schemas/BigProductType'
        trackingType:
          $ref: '#/components/schemas/BigInventoryTrackingType'
        quantity:
          type: integer
          minimum: 0
        jobId:
          type: string
        metadata:
          type: object
          additionalProperties: true
    InventoryReport:
      type: object
      required:
        - timestamp
        - summary
        - byProductType
      properties:
        timestamp:
          type: string
          format: date-time
        summary:
          type: object
          description: Top-level counts across all product types.
          properties:
            totalItems:
              type: integer
            inStock:
              type: integer
            inShippingQueue:
              type: integer
            shipped:
              type: integer
            damaged:
              type: integer
            deleted:
              type: integer
        byProductType:
          type: object
          description: |
            Same shape as `summary`, keyed by `BigProductType` value. Only product
            types with at least one item are present.
          additionalProperties:
            type: object
            properties:
              total:
                type: integer
              inStock:
                type: integer
              inShippingQueue:
                type: integer
              shipped:
                type: integer
              damaged:
                type: integer
        recentActivity:
          type: object
          properties:
            itemsCreatedToday:
              type: integer
            itemsShippedToday:
              type: integer
            itemsReturnedToday:
              type: integer
        lowStockWarnings:
          type: array
          items:
            type: object
            properties:
              productType:
                $ref: '#/components/schemas/BigProductType'
              currentStock:
                type: integer
              threshold:
                type: integer
    Topic:
      type: object
      description: |
        Topic / interest tag in the Bigscreen app. Mirrors `Schemas.Topic`
        (LegacySchemas.ts). Drives subscription discovery; `subscriptions` holds the
        set of subscribed accounts internally and is summarized publicly via
        `subscriptionsCount`.
      required:
        - id
        - name
        - title
        - status
      properties:
        id:
          type: string
          description: 12-character random id.
        createdAt:
          type: string
          format: date-time
        name:
          type: string
          description: URL-friendly slug, generated from the title at creation time.
        title:
          type: string
        description:
          type: string
        status:
          type: string
          description: '`TopicStatus` — typically `ACTIVATED` or `DEACTIVATED`.'
        subscriptions:
          type: object
          additionalProperties: true
          description: Internal map of subscriptions keyed by id.
        subscriptionsCount:
          type: integer
      example:
        id: tpc_abc12345xyz
        createdAt: '2026-04-15T11:09:31.000Z'
        name: live-events
        title: Live Events
        description: Concerts, sports, premieres.
        status: ACTIVATED
        subscriptions: {}
        subscriptionsCount: 142
    Job:
      type: object
      description: |
        Manufacturing job — one per cushion to machine. Mirrors `FabricatorSchemas.Job`.
        Created from a verified `ScanRequest`; advances through `JobState` via `JobAction`
        transitions. Carries the WaxId / NfcTag / toolpath snapshots used by the factory
        pipeline.
      required:
        - id
        - scanRequestId
        - ownerBigscreenAccountId
        - jobType
      properties:
        id:
          type: string
        createdAt:
          type: string
          format: date-time
        ownerBigscreenAccountId:
          type: string
        creatorBigscreenAccountId:
          type: string
        scanRequestId:
          type: string
          description: Source scan request.
        bigOrderId:
          type: string
          description: Convenience pointer to the Big Order.
        shopifyOrderId:
          type: string
          description: Convenience pointer to the Shopify order.
        name:
          type: string
          description: Operator-supplied label.
        jobType:
          type: string
          description: '`FabricatorSchemas.JobType` (e.g. `PersonalizedCushion`, `Unknown`).'
        jobState:
          type: string
          description: |
            State-machine position. Values in `FabricatorSchemas.JobState`
            (`AwaitingInit`, `AwaitingCustomerScan`, `AwaitingScanVerification`,
            `AwaitingToolPathCreation`, `AwaitingManufacture`, `AwaitingQA`,
            `AwaitingNFC`, `AwaitingFulfilment`, `ScanVerificationFailed`,
            `ManufacturingFailed`, etc.). Immutable except via `applyAction`.
        history:
          type: array
          items:
            type: object
            additionalProperties: true
        scanSnapshot:
          type: object
          additionalProperties: true
          description: Frozen copy of the scan that drove this job.
        toolPathSnapshots:
          type: array
          items:
            type: object
            additionalProperties: true
          description: Each entry is a `ToolPath` record; the most recent drives the CNC.
        waxIdSnapshots:
          type: array
          items:
            type: object
            additionalProperties: true
          description: One entry per `WaxId` allocation event.
        toolPathBitFieldSnapshot:
          type: object
          additionalProperties: true
        customerIpd:
          type: integer
          description: IPD specified by the customer on Shopify (-1 if unset).
        preprocessorIpd:
          type: integer
          description: IPD measured by the preprocessor (-1 if unset).
        preprocessorLeftPupil:
          type: array
          items:
            type: number
        preprocessorRightPupil:
          type: array
          items:
            type: number
        preprocessorResult:
          type: string
          description: '`FabricatorSchemas.PreprocessorResult`.'
        ipd:
          type: integer
          description: Final IPD applied to the job.
        ipdSource:
          type: string
          description: '`FabricatorSchemas.BigOrderIpdSource` — where the final IPD came from.'
        waxId:
          type: string
          description: 20-bit BitField identifier stamped on the wax block (allocated under a Redis lock).
        nfcTagId:
          type: string
          description: NFC tag id assigned at the QA station.
        nfcTagSnapshot:
          type: object
          additionalProperties: true
        priority:
          type: integer
          description: Operator-set priority (clamped to `[-1, 100]`).
        stats:
          type: object
          additionalProperties: true
        version:
          type: string
          example: V3
        bigscreenAccountId:
          type: string
          deprecated: true
          description: Legacy field — use `ownerBigscreenAccountId`.
      example:
        id: job_01HXY8ZW7Q9V0K3M5RPQ4ABCDE
        createdAt: '2026-04-09T14:01:32.000Z'
        ownerBigscreenAccountId: acct_01HXJ4Q12345
        creatorBigscreenAccountId: acct_01HXJ4Q12345
        scanRequestId: scan_01HXJ58DQX5N9P2VRC8B3MZ7Y4
        bigOrderId: 01HXJ4S3M0Q3TRBQ3JV9P5W2VC
        shopifyOrderId: '6107853586649'
        jobType: PersonalizedCushion
        jobState: AwaitingToolPathCreation
        history: []
        customerIpd: 63
        preprocessorIpd: 62
        preprocessorLeftPupil:
          - 302.4
          - 478.1
        preprocessorRightPupil:
          - 482.7
          - 478
        preprocessorResult: Success
        ipd: 62
        ipdSource: Preprocessor
        waxId: AB12CD34EF56GH78IJ90
        priority: 0
        version: V3
        toolPathSnapshots: []
        waxIdSnapshots: []
    ScanRequest:
      type: object
      description: |
        Customer face-scan submission — the first step in the fabricator pipeline.
        Mirrors `FabricatorSchemas.ScanRequest`. Created when a Big Order needs custom
        cushions; the customer uploads scan data via the iOS app, the preprocessor
        measures IPD + pupil locations, then the request transitions to
        `ReadyForFabricator` and `Job`s are spawned from it.
      required:
        - id
        - ownerBigscreenAccountId
        - bigOrderId
        - scanRequestState
      properties:
        id:
          type: string
        createdAt:
          type: string
          format: date-time
        ownerBigscreenAccountId:
          type: string
          description: Customer account that owns the scan.
        creatorBigscreenAccountId:
          type: string
          description: Account that created the request.
        bigOrderId:
          type: string
          description: Originating Big Order.
        topologyPatientId:
          type: string
          description: Patient id forwarded to the Topology service for prescription alignment.
        topologyAuthToken:
          type: string
          description: Auth token used to fetch Topology data for this patient.
        name:
          type: string
          description: Operator-supplied label.
        scanRequestState:
          type: string
          description: |
            State-machine position. Values in `FabricatorSchemas.ScanRequestState`
            (`Idle`, `AwaitingCustomer`, `AwaitingPreprocessor`, `ReadyForFabricator`,
            `PreprocessorFailed`, `Cancelled`, etc.). Immutable except via `applyAction`.
        history:
          type: array
          items:
            type: object
            additionalProperties: true
        scanSnapshot:
          type: object
          additionalProperties: true
          description: Snapshot of the most recent uploaded scan.
        shopifyIpd:
          type: integer
          description: IPD as specified by the customer on Shopify (-1 if unset).
        preprocessorIpd:
          type: integer
          description: IPD measured by the preprocessor (-1 if not yet run).
        preprocessorLeftPupil:
          type: array
          items:
            type: number
          description: Left pupil pixel coordinates from the preprocessor.
        preprocessorRightPupil:
          type: array
          items:
            type: number
        preprocessorResult:
          type: string
          description: '`FabricatorSchemas.PreprocessorResult` — outcome of the most recent preprocessor run.'
        ipd:
          type: integer
          description: Final IPD value (-1 if not determined).
        jobIds:
          type: array
          items:
            type: string
          description: Jobs spawned from this scan (loose linkage — jobs can outlive the request).
        version:
          type: string
          example: V2
        scanRequestType:
          type: string
          description: |
            `FabricatorSchemas.ScanRequestType` — `Default` for full scan flows,
            `IpdOnly` when the request is just for an IPD update without a new
            cushion job.
      example:
        id: scan_01HXJ58DQX5N9P2VRC8B3MZ7Y4
        createdAt: '2026-04-08T18:05:42.000Z'
        ownerBigscreenAccountId: acct_01HXJ4Q12345
        creatorBigscreenAccountId: acct_01HXJ4Q12345
        bigOrderId: 01HXJ4S3M0Q3TRBQ3JV9P5W2VC
        name: First scan
        scanRequestState: ReadyForFabricator
        shopifyIpd: 63
        preprocessorIpd: 62
        preprocessorLeftPupil:
          - 302.4
          - 478.1
        preprocessorRightPupil:
          - 482.7
          - 478
        preprocessorResult: Success
        ipd: 62
        jobIds:
          - job_01HXY8ZW7Q9V0K3M5RPQ4ABCDE
        version: V2
        scanRequestType: Default
        history: []
    KBQueryResult:
      type: object
      description: |
        Result of a natural-language query to the knowledge base — the LLM-generated
        answer plus the source chunks that were retrieved to ground it. Returned by
        `POST /admin/kb/query`.
      required:
        - answer
        - sources
        - metadata
      properties:
        answer:
          type: string
          description: LLM-generated answer to the user's query.
        sources:
          type: array
          description: Document chunks the answer was grounded in.
          items:
            type: object
            properties:
              title:
                type: string
              sourceType:
                type: string
                description: e.g. `discord`, `github`, `document`.
              sourceUrl:
                type: string
              relevance:
                type: number
                description: Cosine similarity between query and chunk (0–1).
              excerpt:
                type: string
                description: First ~200 chars of the chunk; only present when `options.includeContext` is true.
        metadata:
          type: object
          properties:
            retrievalTimeMs:
              type: integer
            generationTimeMs:
              type: integer
            totalTimeMs:
              type: integer
      example:
        answer: The shipping pipeline is split between BigShipperApi and ShippingAdminApi. BigShipperApi handles the picking/packing state machine; ShippingAdminApi covers Shippo integration and analytics.
        sources:
          - title: Shipping module overview
            sourceType: document
            sourceUrl: docs/services/admin-api/shipping.md
            relevance: 0.91
          - title: BigShipperApi handler
            sourceType: github
            sourceUrl: https://github.com/bigscreenvr/cloud/blob/master/api/src/fabricator/ShippingAdminApi.ts
            relevance: 0.84
        metadata:
          retrievalTimeMs: 142
          generationTimeMs: 1482
          totalTimeMs: 1624
    KBSearchResult:
      type: object
      description: |
        Search result from `POST /admin/kb/search` — the document chunks that match
        the query, without the LLM generation step. Optionally re-ranked by Cohere.
      required:
        - results
        - metadata
      properties:
        results:
          type: array
          items:
            type: object
            properties:
              chunkId:
                type: string
              documentId:
                type: string
              title:
                type: string
              sourceType:
                type: string
              sourceUrl:
                type: string
              relevance:
                type: number
                description: Cosine similarity (0–1).
              content:
                type: string
                description: Full chunk content.
              metadata:
                type: object
                additionalProperties: true
                description: Source-specific metadata.
        metadata:
          type: object
          properties:
            totalTimeMs:
              type: integer
            resultCount:
              type: integer
      example:
        results:
          - chunkId: chk_01HZS12345
            documentId: doc_01HZS67890
            title: Shipping module overview
            sourceType: document
            sourceUrl: docs/services/admin-api/shipping.md
            relevance: 0.91
            content: The shipping pipeline is split between BigShipperApi (picking + packing state machine) and ShippingAdminApi (Shippo integration, analytics)...
            metadata:
              path: docs/services/admin-api/shipping.md
        metadata:
          totalTimeMs: 142
          resultCount: 1
    KBIngestionResult:
      type: object
      description: |
        Result of a multi-source ingestion run (GitHub, sync, cleanup-commits).
        Reports per-source counts plus any non-fatal errors surfaced during the run.
        Returned by `POST /admin/kb/ingest/github`, `POST /admin/kb/ingest/github/cleanup-commits`,
        and `GET /admin/kb/sync`.
      required:
        - success
      properties:
        success:
          type: boolean
        autoDiscovered:
          type: boolean
          description: Whether the run discovered repos automatically vs using an explicit list.
        reposSynced:
          type: integer
        documentsSynced:
          type: integer
        documentsDeleted:
          type: integer
        commitsSynced:
          type: integer
        wikiPagesSynced:
          type: integer
        syncStatesCleared:
          type: integer
          description: Cleared rows from `kb_sync_state` (cleanup runs only).
        message:
          type: string
          description: Human-readable summary, especially after cleanup runs.
        errors:
          type: array
          items:
            type: string
          description: Per-source / per-document non-fatal errors.
      example:
        success: true
        autoDiscovered: true
        reposSynced: 4
        documentsSynced: 102
        documentsDeleted: 3
        commitsSynced: 87
        wikiPagesSynced: 21
        errors: []
    KBDocument:
      type: object
      description: |
        A single ingested document in the knowledge base — one row per source-side
        artifact (a markdown file, a Discord message thread, a GitHub issue, etc.).
        Mirrors `kb_documents`. Documents are split into `kb_chunks` for retrieval;
        `chunkCount` and the chunk array are returned by
        `GET /admin/kb/document/{documentId}` and not by the listing endpoint.
      required:
        - id
        - sourceType
        - sourceId
        - title
      properties:
        id:
          type: string
          description: KB document UUID.
        sourceType:
          type: string
          description: Where the doc came from — `discord`, `github`, `document`, etc.
        sourceId:
          type: string
          description: Source-side identifier (e.g. file path, message id).
        sourceUrl:
          type: string
          description: Optional URL pointing back to the source.
        title:
          type: string
        contentType:
          type: string
          description: MIME-ish hint (e.g. `text/markdown`, `text/plain`).
        visibility:
          type: string
          description: Visibility class (e.g. `public`, `internal`).
        allowedGroups:
          type: array
          items:
            type: string
          description: When set, only callers with at least one matching access policy can retrieve this doc.
        indexedAt:
          type: string
          format: date-time
          description: When the chunker / embedder last processed this document.
        sourceCreatedAt:
          type:
            - string
            - 'null'
          format: date-time
          description: Timestamp on the source side, when known.
        metadata:
          type: object
          additionalProperties: true
          description: Source-specific metadata.
      example:
        id: doc_01HZS67890ABC123DEF45GH678
        sourceType: document
        sourceId: docs/services/admin-api/shipping.md
        sourceUrl: https://github.com/bigscreenvr/cloud/blob/master/docs/services/admin-api/shipping.md
        title: Shipping module overview
        contentType: text/markdown
        visibility: internal
        allowedGroups: []
        indexedAt: '2026-04-22T10:14:00.000Z'
        sourceCreatedAt: '2026-04-15T11:00:00.000Z'
        metadata:
          path: docs/services/admin-api/shipping.md
    KBStats:
      type: object
      description: |
        Knowledge-base statistics — total document and chunk counts plus a
        per-source-type breakdown. Returned by `GET /admin/kb/stats`. Counts are
        computed live from `kb_documents` / `kb_chunks`.
      required:
        - documents
        - chunks
        - bySource
      properties:
        documents:
          type: integer
          description: Total documents indexed.
        chunks:
          type: integer
          description: Total chunks (post-splitting) across all documents.
        bySource:
          type: object
          description: Document count keyed by `sourceType`.
          additionalProperties:
            type: integer
      example:
        documents: 1842
        chunks: 21407
        bySource:
          document: 312
          github: 1290
          discord: 240
    KBQueryLog:
      type: object
      description: |
        A single entry from `kb_query_logs` — what the user asked, how long retrieval
        + generation took, the resulting feedback score (if any), and who asked.
        Returned by `GET /admin/kb/logs`. Updated by `POST /admin/kb/feedback/{id}`.
      required:
        - id
        - queryText
        - totalTimeMs
        - createdAt
      properties:
        id:
          type: string
        queryText:
          type: string
        retrievalTimeMs:
          type: integer
        generationTimeMs:
          type: integer
        totalTimeMs:
          type: integer
        feedbackScore:
          type:
            - integer
            - 'null'
          description: 1 = thumbs-up, -1 = thumbs-down, null = no feedback.
        feedbackText:
          type:
            - string
            - 'null'
          description: Free-text follow-up from the user (when feedback was submitted).
        userId:
          type:
            - string
            - 'null'
          description: Bigscreen account id of the asker, if known.
        createdAt:
          type: string
          format: date-time
      example:
        id: qlog_01HZT12345ABC678DEF90GHJKM
        queryText: Where do shipment labels get generated?
        retrievalTimeMs: 142
        generationTimeMs: 1482
        totalTimeMs: 1624
        feedbackScore: 1
        feedbackText: null
        userId: acct_01HXJ4Q12345
        createdAt: '2026-04-22T10:14:00.000Z'
    Channel:
      type: object
      description: |
        Public channel — `LegacySchemas.PublicChannel` from
        [`api/src/LegacySchemas.ts`](../../../../api/src/LegacySchemas.ts).
        Returned by `/channel/{channelId}` and embedded inside
        `ChannelGroup.channels`. The full admin `Channel` (with `streams`,
        `queries`, `geo`, `ageRestriction`, etc.) is **not** what this surface
        returns — admin-only fields are stripped.
      required:
        - id
        - name
      properties:
        id:
          type: string
          description: Channel UUID.
        name:
          type: string
          description: URL-friendly slug.
        description:
          type: string
        title:
          type: string
          description: Display title.
        roomSettings:
          type: object
          description: Free-form per-channel room settings the VR app applies on join.
          additionalProperties: true
        texture:
          type: object
          description: '`Texture` instance — image asset metadata for the channel tile.'
          additionalProperties: true
      example:
        id: 01HXKV5G6PQRS8T9UVWXYZABCD
        name: live-events
        title: Live Events
        description: Live concerts, sports, and special broadcasts.
        roomSettings: {}
        texture: {}
    ChannelGroup:
      type: object
      description: |
        Public channel group — the response shape returned by
        `getPublicChannelGroup` / `getPublicChannelGroups` in
        [`api/src/Channels.ts`](../../../../api/src/Channels.ts). Wraps a
        set of `Channel`s with display metadata. The admin `ChannelGroup`
        (with `channelIds`, `createdAt`, etc.) is the storage shape; this
        is the resolved public projection.
      required:
        - name
        - channels
      properties:
        name:
          type: string
          description: URL-friendly slug.
        title:
          type: string
          description: Display title.
        imageUrl:
          type: string
          format: uri
          description: Optional thumbnail for the group tile.
        isPublished:
          type: boolean
          description: Whether the group is exposed to non-staff clients.
        channels:
          type: array
          items:
            $ref: '#/components/schemas/Channel'
        textures:
          type: array
          description: Generated atlas `Texture` records associated with the group.
          items:
            type: object
            additionalProperties: true
      example:
        name: featured-this-week
        title: Featured This Week
        imageUrl: https://dev-internal-files.bigscreencloud.com/channel-groups/featured.png
        isPublished: true
        channels:
          - id: 01HXKV5G6PQRS8T9UVWXYZABCD
            name: live-events
            title: Live Events
            description: Live concerts, sports, and special broadcasts.
            roomSettings: {}
            texture: {}
        textures: []
    OAuthClient:
      type: object
      description: |
        OAuth 2.0 client registered with the Bigscreen OAuth provider. Mirrors
        `OAuthClientDatabase.OAuthClientRow`. The `clientSecretHash` is **never**
        returned in any response — the plaintext secret is returned exactly once on
        create / rotate (see `OAuthClientCreated` for that response shape).
      required:
        - uniqueId
        - clientId
        - clientType
        - name
        - ownerAccountId
        - createdAt
      properties:
        uniqueId:
          type: string
          description: Internal id used for admin operations and step-up unlocks.
        clientId:
          type: string
          description: Public client identifier sent to the OAuth `/authorize` endpoint.
        clientType:
          type: string
          description: |
            Client type. `confidential` is the only value supported in v1; `public`
            is reserved for a later phase.
          enum:
            - confidential
            - public
        name:
          type: string
        description:
          type:
            - string
            - 'null'
        logoUrl:
          type:
            - string
            - 'null'
          format: uri
        homepageUrl:
          type:
            - string
            - 'null'
          format: uri
        redirectUris:
          type: array
          items:
            type: string
            format: uri
        allowedScopes:
          type: array
          items:
            type: string
          description: Scopes the client may request.
        serverIps:
          type: array
          items:
            type: string
          description: IP allowlist (required — clients must call from one of these IPs).
        securityGroupRuleIds:
          type: array
          items:
            type: string
          description: AWS security-group rule ids backing the IP allowlist.
        ownerAccountId:
          type: string
          description: Bigscreen account that owns the client. Required for ownership-or-SuperUser checks on mutations.
        createdAt:
          type: integer
          format: int64
          description: Creation timestamp in Unix ms.
        createdByAccountId:
          type:
            - string
            - 'null'
        disabledAt:
          type:
            - integer
            - 'null'
          format: int64
          description: When the client was disabled, if disabled.
        disabledReason:
          type:
            - string
            - 'null'
        deletedAt:
          type:
            - integer
            - 'null'
          format: int64
          description: When the client was soft-deleted, if applicable.
      example:
        uniqueId: oac_01HXKV2M3X8FQ7P4ABCDEFGH9
        clientId: arda-internal
        clientType: confidential
        name: Arda
        description: Internal admin webapp
        logoUrl: null
        homepageUrl: https://arda.internal.bigscreenvr.com
        redirectUris:
          - https://arda.internal.bigscreenvr.com/oauth/callback
        allowedScopes:
          - openid
          - profile
          - admin:all
        serverIps:
          - 10.0.42.10
        securityGroupRuleIds:
          - sgr-0a1b2c3d4e5f6789
        ownerAccountId: acct_01HXJ4Q12345
        createdAt: 1747088000000
        createdByAccountId: acct_01HXJ4Q12345
        disabledAt: null
        disabledReason: null
        deletedAt: null
    OAuthGrant:
      type: object
      description: |
        User → client grant — records a Bigscreen account's approval of a specific
        OAuth client and the scopes it consented to. Mirrors `OAuthClientDatabase.OAuthGrantRow`.
        Created or updated via the consent flow at `POST /admin/oauth/my_grant`.
      required:
        - uniqueId
        - oauthGrantId
        - userId
        - clientId
        - scopes
        - grantedAt
      properties:
        uniqueId:
          type: string
          description: Internal grant row id.
        oauthGrantId:
          type: string
          description: Public grant identifier exposed to the consent UI.
        userId:
          type: string
          description: Bigscreen account id that approved the grant.
        clientId:
          type: string
          description: OAuth client this grant authorizes.
        scopes:
          type: array
          items:
            type: string
          description: Scopes the user has approved for this client.
        grantedAt:
          type: integer
          format: int64
          description: Unix ms when the grant was first created.
        updatedAt:
          type: integer
          format: int64
          description: Unix ms of the most recent grant update.
        revokedAt:
          type:
            - integer
            - 'null'
          format: int64
          description: Unix ms when the grant was revoked, if any.
      example:
        uniqueId: oag_01HXKV4ABCDEFGH9KLMN0PQRS
        oauthGrantId: g_01HXKV4ABCDEFGH9KLMN0PQRS
        userId: acct_01HXJ4Q12345
        clientId: arda-internal
        scopes:
          - openid
          - profile
        grantedAt: 1747088000000
        updatedAt: 1747088000000
        revokedAt: null
    BigscreenAccountStatus:
      type: string
      description: |
        Account moderation state. Mirrors `BigscreenAccountStatus` in
        [`auth/Schemas.ts`](../../../../auth/Schemas.ts).
        - `OK` — normal.
        - `ReadOnly` — can sign in and view, but mutations are blocked.
        - `Banned` — sign-in blocked.
      enum:
        - OK
        - ReadOnly
        - Banned
      default: OK
      example: OK
    AdminAccount:
      type: object
      description: |
        Sanitized admin view of a Bigscreen account — `SanitizedAdminAccount`
        from [`auth/Schemas.ts`](../../../../auth/Schemas.ts) — the shape
        returned to admin callers from `/admin/account/*` and
        `/admin/account/query/*`. Built from the raw `BigscreenAccount` minus
        secret fields like password hashes; the Steam / Oculus / EmailPassword
        subobjects are flattened into `email`, `steamProfile`, and
        `oculusProfile`.
      required:
        - id
        - username
      properties:
        id:
          type: string
          description: Bigscreen account UUID.
        createdAt:
          type: string
          format: date-time
        status:
          $ref: '#/components/schemas/BigscreenAccountStatus'
        isVerified:
          type: boolean
          description: Whether the account's primary email is verified.
        isStaff:
          type: boolean
          description: Whether the account belongs to Bigscreen staff.
        username:
          type: string
        lowerCaseUsername:
          type: string
          description: |
            `username` lowercased — surfaced for case-insensitive search.
        email:
          type: string
          format: email
          description: Surfaced from `EmailPasswordAccount.email` when an email identity is attached.
        steamProfile:
          type: object
          description: Steam identity (when attached). Loose shape — Steam fields surfaced as-is.
          additionalProperties: true
        oculusProfile:
          type: object
          description: '`SanitizedOculusProfile` (when an Oculus identity is attached).'
          properties:
            oculusId:
              type: string
            oculusImageURL:
              type: string
              format: uri
            oculusSmallImageURL:
              type: string
              format: uri
        accessPolicies:
          type: array
          items:
            type: string
          description: Access policies the account holds (e.g. `Admin`, `Moderator`, `BigscreenDefault`).
        banHistory:
          type: array
          items:
            type: object
            additionalProperties: true
        adminNotes:
          type: array
          description: '`AdminNote[]` — staff annotations attached to the account.'
          items:
            type: object
            additionalProperties: true
      example:
        id: acct_01HXJ4Q12345
        createdAt: '2026-04-22T10:14:00.000Z'
        status: OK
        isVerified: true
        isStaff: false
        username: alice
        lowerCaseUsername: alice
        email: alice@example.com
        accessPolicies:
          - BigscreenDefault
        banHistory: []
        adminNotes: []
    MediaItem:
      type: object
      description: |
        A media asset that can be played in Bigscreen — typically a movie or series.
        Mirrors `Schemas.MediaItem` (LegacySchemas.ts). The `cdn` / `cdnId` fields point
        to the underlying asset on the configured CDN; the rest is metadata used for
        listing, playback, and entitlement.
      required:
        - id
        - title
        - cdn
        - type
      properties:
        id:
          type: string
          description: UUID.
        title:
          type: string
          description: Display title shown in the dashboard UI.
        createdAt:
          type: string
          format: date-time
        locationCodes:
          type: array
          items:
            type: string
          description: Allowed countries (ISO codes).
        locationCode:
          type: string
          deprecated: true
          description: Legacy single-code field — use `locationCodes`.
        deviceCode:
          type: string
          description: Device restrictions (typically `Unrestricted`).
        cdn:
          type: string
          description: CDN identifier (e.g. `cloudfront`, `bunny`).
        cdnId:
          type: string
          description: Asset id on the CDN.
        cdnDataSnapshot:
          type: object
          additionalProperties: true
          description: Cached CDN metadata for faster queries.
        duration:
          type: integer
          description: Duration in milliseconds.
        closedCaptions:
          type: array
          items:
            type: string
        subtitleLanguages:
          type: array
          items:
            type: string
        audioTracks:
          type: array
          items:
            type: string
        audioFormats:
          type: array
          items:
            type: string
          description: e.g. `5.1`, `Stereo`, `Atmos`.
        type:
          type: string
          description: Media type — `MediaType.MOVIE`, etc.
        metaData:
          type: object
          additionalProperties: true
          description: Free-form metadata about the asset.
        isStereo:
          type: boolean
          description: Whether the asset is stereoscopic 3D.
        stereoPacking:
          type: string
          enum:
            - None
            - LeftRight
            - OverUnder
        stereoWidth:
          type: string
          enum:
            - Default
            - Half
          description: Width adjustment when rendering — `Half` means the source frame is 2× target width.
      example:
        id: 01HXKV7N8PQRSTU9VWXYZABCDE
        title: Big Buck Bunny
        createdAt: '2026-04-15T11:09:31.000Z'
        locationCodes:
          - us
          - de
          - fr
          - jp
          - ca
        locationCode: ''
        deviceCode: Unrestricted
        cdn: cloudfront
        cdnId: abc123
        duration: 596000
        closedCaptions:
          - en
        subtitleLanguages:
          - en
        audioTracks:
          - en
        audioFormats:
          - '5.1'
        type: movie
        metaData: {}
        isStereo: false
        stereoPacking: None
        stereoWidth: Default
    MediaProduct:
      type: object
      description: |
        A purchasable bundle of one or more `MediaItem`s. Mirrors
        `Schemas.MediaProduct` (LegacySchemas.ts). Carries marketing copy, pricing
        integrations (Stripe / Oculus IAP), entitlement class, and synchronization
        interval for rooms.
      required:
        - id
        - bigMediaId
        - status
        - title
        - mediaItemIds
        - entitlementClass
      properties:
        id:
          type: string
          description: Internal UUID.
        createdAt:
          type: string
          format: date-time
        bigMediaId:
          type: string
          description: Public identifier — anyone can use this to request the product.
        status:
          type: string
          description: '`ProductStatus` value — typically `DRAFT`, `PUBLISHED`, or `RETIRED`.'
        title:
          type: string
          description: Display title (also used as the Stripe product name).
        description:
          type: string
        url:
          type: string
          description: Human-readable URL slug.
        mediaItemIds:
          type: array
          items:
            type: string
          description: Media items bundled in this product.
        startingMediaItemId:
          type:
            - string
            - 'null'
          description: For movie nights — the media item to play first.
        featuredMediaItemId:
          type:
            - string
            - 'null'
          description: For movie nights — the featured "main feature" item.
        duration:
          type: integer
          description: Combined duration of all media items, in milliseconds.
        loops:
          type: boolean
          description: Whether the media loops on playback.
        is3D:
          type: boolean
          description: Indicates the main feature is stereoscopic.
        marketingData:
          type: object
          additionalProperties: true
          description: |
            Per-locale marketing payload. Keyed by locale (e.g. `en-US`); values are
            `ProductMarketing` records (titles, descriptions, image / texture URLs).
        locationCodes:
          type: array
          items:
            type: string
        deviceCode:
          type: string
        stripeProductData:
          type: object
          additionalProperties: true
          description: Cached Stripe product object (https://stripe.com/docs/api/products/object).
        oculusProductData:
          type: object
          additionalProperties: true
          description: Cached Oculus IAP product info.
        entitlementClass:
          type: string
          description: Maps to what's shown on the purchase page (e.g. `TWO_DAY_RENTAL`, `OWNERSHIP`).
        synchronizationInterval:
          type: integer
          description: |
            Interval (minutes, 0–30) used to generate showing time slots. `0` means the
            sync timestamp is set when the first person joins the room.
        roomSettings:
          type: object
          additionalProperties: true
      example:
        id: 01HXKV8QRSTU9VWXYZABCDEFGH
        createdAt: '2026-04-15T11:09:31.000Z'
        bigMediaId: bbb-2008
        status: PUBLISHED
        title: Big Buck Bunny
        description: Open-source animated short.
        url: big-buck-bunny
        mediaItemIds:
          - 01HXKV7N8PQRSTU9VWXYZABCDE
        startingMediaItemId: null
        featuredMediaItemId: 01HXKV7N8PQRSTU9VWXYZABCDE
        duration: 596000
        loops: false
        is3D: false
        locationCodes:
          - us
          - de
          - fr
          - jp
          - ca
        deviceCode: Unrestricted
        entitlementClass: TWO_DAY_RENTAL
        synchronizationInterval: 15
    Entitlement:
      type: object
      description: |
        A media entitlement — entitles a Bigscreen account to consume a specific media
        product. Mirrors `Schemas.Entitlement` (api/src/Schemas.ts). Created when a
        purchase, IAP, coupon redemption, or admin grant succeeds; transitions through
        a status state machine (`IDLE` → `ACTIVATED` → expired) and may carry session
        history.
      required:
        - id
        - bigscreenAccountId
        - mediaProductId
        - bigMediaId
        - entitlementClass
        - status
      properties:
        id:
          type: string
        createdAt:
          type: string
          format: date-time
        bigscreenAccountId:
          type: string
          description: Owner — the customer the entitlement belongs to.
        ip:
          type: string
          description: Customer IP captured at order time.
        receiptId:
          type: string
          description: Internal receipt id minted at purchase confirmation.
        receiptSnapshot:
          type: object
          additionalProperties: true
          description: Snapshot of the receipt at the time of purchase (auditing).
        mediaProductId:
          type: string
          description: Internal media product id. Immutable.
        bigMediaId:
          type: string
          description: Public media product id. Immutable.
        productSnapshot:
          type: object
          additionalProperties: true
          description: Snapshot of the product at purchase time.
        entitlementClass:
          type: string
          description: |
            Governs what happens when first used. Common values include
            `TWO_DAY_RENTAL`, `OWNERSHIP`, `FREE`, `PROMOTIONAL`. See `EntitlementClass`.
        status:
          type: string
          description: |
            State-machine position. `IDLE` (not yet activated), `ACTIVATED`,
            `EXPIRED`, `REFUNDED`, etc.
        activationTimestamp:
          type:
            - string
            - 'null'
          format: date-time
          description: When the entitlement was first activated. Null while `IDLE`. Set once.
        expirationTimestamp:
          type:
            - string
            - 'null'
          format: date-time
          description: When the entitlement expires. Set when activated for time-limited classes.
        sessions:
          type: array
          items:
            type: object
            additionalProperties: true
            description: Per-session record (device id, started at, ended at, etc.).
        maxAllowedSessions:
          type: integer
          description: '-1 indicates unlimited.'
        deviceHistory:
          type: array
          items:
            type: object
            additionalProperties: true
          description: Devices that have used this entitlement; empty if never used.
        checksum:
          type: string
          description: MD5 over `bigscreenAccountId` + `receiptId` + media. Internal integrity check.
      example:
        id: ent_01HZB12345ABC678DEF90GHJKM
        createdAt: '2026-04-22T10:14:00.000Z'
        bigscreenAccountId: acct_01HXJ4Q12345
        ip: 203.0.113.10
        receiptId: rcpt_01HZB67890XYZ123
        mediaProductId: 01HXKV8QRSTU9VWXYZABCDEFGH
        bigMediaId: bbb-2008
        entitlementClass: TWO_DAY_RENTAL
        status: ACTIVATED
        activationTimestamp: '2026-04-22T11:00:00.000Z'
        expirationTimestamp: '2026-04-24T11:00:00.000Z'
        sessions: []
        maxAllowedSessions: -1
        deviceHistory: []
        checksum: a3b2c1d4e5f60718
    MediaCategory:
      type: object
      description: |
        A media category — a top-level grouping in the Bigscreen TV / store UI that
        contains one or more `ProductCollection`s. Mirrors `Schemas.ProductCategory`
        (LegacySchemas.ts). Drives the home-screen rails and the unity thumbnail
        textures rendered on top of them.
      required:
        - id
        - name
        - title
        - entitlementClass
      properties:
        id:
          type: string
        createdAt:
          type: string
          format: date-time
        name:
          type: string
          deprecated: true
          description: Internal name. Obsolete — use ids instead.
        urlName:
          type: string
          description: URL slug; auto-updated when `title` changes.
        title:
          type: string
          description: Visible category name.
        description:
          type: string
        productCollectionIds:
          type: array
          items:
            type: string
          description: Collections that belong to this category.
        entitlementClass:
          type: string
          description: '`EntitlementClass` value the category surfaces (e.g. `TWO_DAY_RENTAL`, `OWNERSHIP`).'
        unityTextureConfig:
          type: object
          properties:
            imageWidth:
              type: integer
              default: 173
            imageHeight:
              type: integer
              default: 256
            canvasWidth:
              type: integer
              default: 512
            canvasHeight:
              type: integer
              default: 512
          description: Image and canvas sizes used when generating the unity thumbnail texture.
        isScheduled:
          type: boolean
          description: Whether the category honors the per-collection scheduling windows.
        defaultCollectionIndex:
          type:
            - integer
            - 'null'
          description: Index into `productCollectionIds` to surface by default.
      example:
        id: 01HZD12345ABC678DEF90GHJKM
        createdAt: '2026-04-22T10:14:00.000Z'
        name: rentals
        urlName: rentals
        title: Rentals
        description: Movies and shows available to rent.
        productCollectionIds:
          - 01HZE12345ABC678DEF90GHJKM
        entitlementClass: TWO_DAY_RENTAL
        unityTextureConfig:
          imageWidth: 173
          imageHeight: 256
          canvasWidth: 512
          canvasHeight: 512
        isScheduled: true
        defaultCollectionIndex: 0
    ProductCollection:
      type: object
      description: |
        A scheduled collection of `MediaProduct`s — the second-level grouping under
        `MediaCategory`. Mirrors `Schemas.ProductCollection` (LegacySchemas.ts). Has
        an availability window so curators can stage holiday rails, premieres, etc.
        in advance.
      required:
        - id
        - name
        - mediaProductIds
      properties:
        id:
          type: string
        createdAt:
          type: string
          format: date-time
        name:
          type: string
          description: Internal Bigscreen name.
        startTimestamp:
          type:
            - string
            - 'null'
          format: date-time
          description: Availability window opens.
        endTimestamp:
          type:
            - string
            - 'null'
          format: date-time
          description: Availability window closes.
        mediaProductIds:
          type: array
          items:
            type: string
          description: Products in this collection, ordered.
        isPublished:
          type: boolean
          description: Whether the collection is currently surfaced.
        textures:
          type: array
          items:
            type: object
            additionalProperties: true
          description: Generated atlas textures the unity client renders.
      example:
        id: 01HZE12345ABC678DEF90GHJKM
        createdAt: '2026-04-22T10:14:00.000Z'
        name: spring-2026
        startTimestamp: '2026-04-01T00:00:00.000Z'
        endTimestamp: '2026-06-30T23:59:59.000Z'
        mediaProductIds:
          - 01HXKV8QRSTU9VWXYZABCDEFGH
        isPublished: true
        textures: []
    Coupon:
      type: object
      description: |
        A coupon — a short-circuit around the payment system. Redeeming a coupon
        produces an `Entitlement` for either an entire entitlement class or a
        specific `bigMediaId`. Mirrors `Schemas.Coupon` (api/src/Schemas.ts).

        Coupons are not credits — they don't carry monetary value, just the right to
        skip checkout for a particular product.
      required:
        - id
        - code
        - entitlementClass
      properties:
        id:
          type: string
        createdAt:
          type: string
          format: date-time
        code:
          type: string
          description: Public code customers type in to redeem. Unique. Auto-generated when not supplied.
        entitlementClass:
          type: string
          description: General entitlement class the coupon redeems for (`TWO_DAY_RENTAL`, `OWNERSHIP`, etc.).
        bigMediaId:
          type:
            - string
            - 'null'
          description: |
            If set, restricts the coupon to a single media product. Overrides the
            broader `entitlementClass`-based eligibility.
        activationTimestamp:
          type:
            - string
            - 'null'
          format: date-time
          description: When the coupon was redeemed. Null while unused.
        expirationTimestamp:
          type:
            - string
            - 'null'
          format: date-time
          description: After this date the coupon can no longer be redeemed.
        entitlementId:
          type:
            - string
            - 'null'
          description: The `Entitlement` that was created on redemption. Null while unused.
      example:
        id: cpn_01HZC12345ABC678DEF90GHJKM
        createdAt: '2026-04-22T10:14:00.000Z'
        code: BIGSCREEN-PROMO-2026
        entitlementClass: TWO_DAY_RENTAL
        bigMediaId: null
        activationTimestamp: null
        expirationTimestamp: '2026-12-31T23:59:59.000Z'
        entitlementId: null
    DHLInvoice:
      type: object
      description: |
        A DHL invoice — created (or upserted) when a MyBill CSV is uploaded via
        `POST /admin/dhl/invoices/upload-csv`. Carries the totals across all
        `DHLCharge` rows that share its `invoiceNumber`.
      required:
        - id
        - invoiceNumber
      properties:
        id:
          type: string
        createdAt:
          type: string
          format: date-time
        invoiceNumber:
          type: string
          description: DHL invoice number (used as the upsert key).
        invoiceDate:
          type: string
          format: date
          description: Date on the invoice itself, not the upload time.
        status:
          type: string
          description: Operator-set status (e.g. `Pending`, `Reviewed`, `Closed`).
        totalAmount:
          type: number
          description: Sum of all `DHLCharge.totalCharge` values for this invoice.
        chargeCount:
          type: integer
          description: COUNT(*) of charge rows linked to this invoice.
        uploadedBy:
          type: string
          description: Account that uploaded the CSV.
        creatorBigscreenAccountId:
          type: string
          description: Account that initially created the invoice row.
      example:
        id: 01HZ12345ABC678DEF90GHJKMN
        createdAt: '2026-04-22T10:14:00.000Z'
        invoiceNumber: MYBILL-2026-04-USA-001
        invoiceDate: '2026-04-15'
        status: Pending
        totalAmount: 18234.56
        chargeCount: 142
        uploadedBy: acct_01HXJ4Q12345
        creatorBigscreenAccountId: acct_01HXJ4Q12345
    DHLCharge:
      type: object
      description: |
        A single DHL shipping charge — one row per tracking number on a DHL invoice.
        Created during the upload-CSV flow, upserted on `(invoiceId, trackingNumber)`.
        `feeBreakdown` is parsed from the CSV's miscellaneous fee columns.
      required:
        - id
        - invoiceId
        - invoiceNumber
        - trackingNumber
      properties:
        id:
          type: string
        createdAt:
          type: string
          format: date-time
        invoiceId:
          type: string
        invoiceNumber:
          type: string
        trackingNumber:
          type: string
        amount:
          type: number
          description: Pre-fee charge amount.
        currency:
          type: string
          example: USD
        serviceType:
          type: string
          description: DHL service code (e.g. `EXPRESS WORLDWIDE`).
        weight:
          type: number
          description: Package weight in kg or lb (per CSV).
        dimensions:
          type: object
          properties:
            length:
              type: number
            width:
              type: number
            height:
              type: number
        description:
          type: string
        origin:
          type: string
          description: Origin code or city.
        destination:
          type: string
          description: Destination code or country.
        totalCharge:
          type: number
          description: Final charge after surcharges; falls back to `amount + fuelSurcharge` if not in the CSV.
        feeBreakdown:
          type: object
          description: |
            Parsed from miscellaneous CSV columns (`fuelSurcharge`, `weightCharge`,
            `taxAdjustment`, `invoiceFee`, `otherCharges*`, `discount*`, `xc*`).
          properties:
            fuelSurcharge:
              type: number
            weightCharge:
              type: number
            taxAdjustment:
              type: number
            invoiceFee:
              type: number
            otherCharges:
              type: array
              items:
                type: number
            discounts:
              type: array
              items:
                type: number
            extraCharges:
              type: array
              items:
                type: number
      example:
        id: 01HZ7890ABC678DEF90GHJKMN
        createdAt: '2026-04-22T10:14:00.000Z'
        invoiceId: 01HZ12345ABC678DEF90GHJKMN
        invoiceNumber: MYBILL-2026-04-USA-001
        trackingNumber: '9361269903500987654321'
        amount: 86.42
        currency: USD
        serviceType: EXPRESS WORLDWIDE
        weight: 2.4
        dimensions:
          length: 30
          width: 25
          height: 18
        description: Bigscreen Beyond
        origin: USA
        destination: DEU
        totalCharge: 102.18
        feeBreakdown:
          fuelSurcharge: 12.84
          weightCharge: 0
          taxAdjustment: 2.92
          invoiceFee: 0
          otherCharges: []
          discounts: []
          extraCharges: []
    DHLChargeFlag:
      type: object
      description: |
        A reconciliation flag on a `DHLCharge`. Created by `POST /admin/dhl/reconcile`
        when a charge differs from the rate Bigscreen quoted at label-purchase time, or
        when the tracking number can't be matched. Updated by operators via
        `PUT /admin/dhl/charges/{id}` to change `status` / `notes`.
      required:
        - id
        - chargeId
        - reason
        - status
      properties:
        id:
          type: string
        createdAt:
          type: string
          format: date-time
        chargeId:
          type: string
          description: The `DHLCharge.id` this flag applies to.
        invoiceId:
          type: string
        trackingNumber:
          type: string
        reason:
          type: string
          enum:
            - TrackingNotFound
            - NoQuotedRate
            - OverThreshold
          description: |
            - `TrackingNotFound` — no matching shipment in our records.
            - `NoQuotedRate` — we don't have a quoted rate for the matched shipment.
            - `OverThreshold` — quoted vs. invoiced delta exceeds the threshold passed to `POST /admin/dhl/reconcile`.
        status:
          type: string
          enum:
            - New
            - Reviewed
            - Resolved
            - Disputed
          description: Operator-set workflow state.
        notes:
          type: string
          description: Operator-supplied notes attached during review.
        quotedRate:
          type: number
          description: Quoted rate captured when the label was bought (if known).
        invoicedAmount:
          type: number
          description: The DHL-invoiced amount the flag is comparing against.
      example:
        id: 01HZF12345ABC678DEF90GHJKM
        createdAt: '2026-04-22T10:18:00.000Z'
        chargeId: 01HZ7890ABC678DEF90GHJKMN
        invoiceId: 01HZ12345ABC678DEF90GHJKMN
        trackingNumber: '9361269903500987654321'
        reason: OverThreshold
        status: New
        notes: ''
        quotedRate: 78.4
        invoicedAmount: 102.18
    BigLineItem:
      type: object
      description: |
        A line item belonging to a `BigOrder`. Mirrors `FabricatorSchemas.BigLineItem` —
        one row per Shopify line item, enriched with shipping group + product type for
        fabricator routing.
      required:
        - id
        - ownerBigscreenAccountId
        - bigOrderId
        - shopifyOrderId
        - shopifyOrderLineItemId
        - type
      properties:
        id:
          type: string
        createdAt:
          type: string
          format: date-time
        ownerBigscreenAccountId:
          type: string
          description: Customer account that owns the line item.
        creatorBigscreenAccountId:
          type: string
          description: Account that created the row.
        bigOrderId:
          type: string
        shopifyOrderId:
          type: string
        shopifyOrderLineItemId:
          type: string
        shopifyOrderLineItemSnapshot:
          type: object
          additionalProperties: true
          description: Snapshot of the Shopify line-item payload at creation time.
        shopifyProductId:
          type: integer
          format: int64
        shopifySku:
          type: object
          additionalProperties: true
          description: Parsed Bigscreen SKU descriptor — `FabricatorSchemas.ShopifySku`.
        type:
          description: Product taxonomy.
          $ref: '#/components/schemas/BigProductType'
        version:
          type: string
          example: V3
        shipmentPackageType:
          type: string
          description: Packaging slot — `FabricatorSchemas.BigShipmentPackageType`.
          example: Default
        shipmentGroupId:
          type: string
          description: Groups items that ship together; empty for un-grouped items.
        metaData:
          type: object
          additionalProperties: true
    BigOrder:
      type: object
      description: |
        Bigscreen's first-party order — mirrors a Shopify order and is enriched with
        fabricator state (line items, shipping summary, checklist result, IPD,
        state-machine position). Mirrors `FabricatorSchemas.BigOrder`.

        Created by `POST /admin/shop/order` once a Shopify order is fulfillable, then
        advanced through actions on `PUT /admin/shop/order/{bigOrderId}`. Most fields
        on this object are populated by the checklist pipeline rather than the caller.
      required:
        - id
        - ownerBigscreenAccountId
        - creatorBigscreenAccountId
        - shopifyOrderId
        - shopifyCustomerId
        - state
      properties:
        id:
          type: string
          description: BigOrder id (UUID).
        createdAt:
          type: string
          format: date-time
        ownerBigscreenAccountId:
          type: string
          description: Customer (the buyer's Bigscreen account).
        creatorBigscreenAccountId:
          type: string
          description: Account that triggered creation (often the same as owner for self-checkout).
        shopifyOrderId:
          type: string
          description: Numeric Shopify order id.
        shopifyCustomerId:
          type: string
          description: Numeric Shopify customer id.
        shopifyOrderName:
          type: string
          description: Shopify's human-readable order name (e.g. `#1234`).
        shopifyOrderSnapshot:
          type: object
          additionalProperties: true
          description: Snapshot of the Shopify order payload at last sync. Shape per Shopify Admin API.
        shopifyOrderStatus:
          type: string
          description: Cached `ShopifyOrder.financial_status` / `fulfillment_status` summary.
        lineItems:
          type: array
          deprecated: true
          items:
            $ref: '#/components/schemas/BigLineItem'
          description: Deprecated — use `currentLineItems`.
        lineItemsV1:
          type: array
          deprecated: true
          items:
            $ref: '#/components/schemas/BigLineItem'
          description: Backup of the deprecated V1 line items.
        lineItemsV2:
          type: array
          deprecated: true
          items:
            $ref: '#/components/schemas/BigLineItem'
        currentLineItems:
          type: array
          items:
            $ref: '#/components/schemas/BigLineItem'
          description: The current (unfulfilled) line items on the order.
        lineItemSummary:
          type: object
          additionalProperties: true
          description: Snapshot of the line-item layout at the last checklist run, including fulfilled and refunded items.
        origin:
          type: string
          description: Origin of the order — `FabricatorSchemas.BigOrderOrigin` (e.g. `Customer`, `TestData`, `Unknown`).
        state:
          type: string
          description: State-machine position — `FabricatorSchemas.BigOrderState`. Immutable except via `applyAction`.
        nextAction:
          type: string
          description: |
            Next action the checklist computed. Values in `FabricatorSchemas.BigOrderAction`
            (e.g. `SendScanRequest`, `AdminOverrideIPD`, `CreateJob`, `CreateShipment`,
            `ShipIt`, `Complete`, `Unknown`).
        priority:
          type: integer
          description: Operator-set priority (clamped server-side to `[-1, 100]`).
        purpose:
          type: string
          description: Free-text purpose tag used in test orders.
        name:
          type: string
          description: Operator-supplied label.
        level:
          type: string
          example: ShopifyOrder
        version:
          type: string
          example: V3
        checklistResult:
          type: object
          additionalProperties: true
          description: Last checklist result stored on the order.
        ipd:
          type: integer
          description: Final IPD (-1 if unset).
        ipdSource:
          type: string
          description: '`FabricatorSchemas.BigOrderIpdSource` — where the IPD value came from (`Customer`, `Admin`, `Preprocessor`, `Unknown`).'
        history:
          type: array
          items:
            type: object
            additionalProperties: true
          description: Append-only audit trail.
        tags:
          type: array
          items:
            type: string
        shipmentHold:
          description: If set, blocks shipment creation. `FabricatorSchemas.BigOrderShipmentHold`.
        shippingServiceLevel:
          type: string
          description: Selected service level (e.g. `Standard`, `Express`).
      example:
        id: 01HXJ4S3M0Q3TRBQ3JV9P5W2VC
        createdAt: '2026-04-08T17:42:11.000Z'
        ownerBigscreenAccountId: acct_01HXJ4Q12345
        creatorBigscreenAccountId: acct_01HXJ4Q12345
        shopifyOrderId: '6107853586649'
        shopifyCustomerId: '7421003628825'
        shopifyOrderName: '#10042'
        shopifyOrderStatus: paid
        origin: Customer
        state: AwaitingScanRequest
        nextAction: SendScanRequest
        priority: 0
        purpose: ''
        name: ''
        level: ShopifyOrder
        version: V3
        ipd: -1
        ipdSource: Unknown
        tags: []
        history: []
        shippingServiceLevel: Standard
        currentLineItems: []
    BigShipment:
      type: object
      description: |
        A shipment — the unit that moves through the picking / packing / labeling /
        dispatch state machine. Mirrors `FabricatorSchemas.BigShipment`. Driven by
        `PUT /admin/shipper/shipment/{id}` actions; one shipment per shipment group
        on a `BigOrder`.
      required:
        - id
        - bigOrderId
        - shopifyOrderId
        - status
      properties:
        id:
          type: string
          description: 10-digit shipment id (custom format generated server-side).
        createdAt:
          type: string
          format: date-time
        creatorBigscreenAccountId:
          type: string
          description: Account that created the shipment.
        bigOrderId:
          type: string
        shopifyOrderId:
          type: string
        shopifyShippingMetaData:
          type: object
          additionalProperties: true
          description: Snapshot of Shopify shipping address and related metadata.
        status:
          type: string
          description: |
            State-machine position. Values in `FabricatorSchemas.BigShipmentStatus`
            (`Init`, `Picking`, `ReadyToPack`, `Packing`, `WaitingForPickup`,
            `Shipped`, `Cancelled`).
        name:
          type: string
          description: Operator-supplied label.
        priority:
          type: integer
          description: Display priority (lower-than-zero values represent unset).
        inventoryItemIds:
          type: array
          items:
            type: string
          description: Inventory items currently attached to this shipment.
        history:
          type: array
          items:
            type: object
            additionalProperties: true
          description: Append-only history; one entry per state transition or significant action.
        currentShippingLabel:
          type: object
          additionalProperties: true
          description: |
            Most recent label payload (Shippo transaction): tracking number, label URL,
            carrier, service, rate. Empty until `GenerateShippingLabel` runs.
        inventorySlots:
          type: array
          items:
            type: object
            additionalProperties: true
          description: Inventory slot manifest — one entry per item the shipment expects.
        shipmentGroupId:
          type: string
          description: Groups this shipment with sibling shipments for the same Big Order.
      example:
        id: '5193045862'
        createdAt: '2026-04-12T22:18:04.000Z'
        creatorBigscreenAccountId: acct_01HXJ4Q12345
        bigOrderId: 01HXJ4S3M0Q3TRBQ3JV9P5W2VC
        shopifyOrderId: '6107853586649'
        status: Picking
        priority: 0
        inventoryItemIds: []
        history:
          - status: Init
            message: Shipment initialized
            createdAt: 1747088284000
        inventorySlots: []
        shipmentGroupId: default
    EmailUpdateTicket:
      type: object
      description: |
        Ticket carrying the verification token an account holder uses to confirm a
        pending email change. Mirrors `AuthSchemas.EmailUpdateTicket`. Returned by
        `GET /admin/account/{bigscreenAccountId}/email_update_ticket` so a moderator
        can hand it to the user when the standard email delivery flow fails.
      required:
        - bigscreenAccountId
        - token
        - newEmail
        - expiryTimeStamp
      properties:
        id:
          type: string
        createdAt:
          type: string
          format: date-time
        bigscreenAccountId:
          type: string
        token:
          type: string
          description: Single-use confirmation token.
        newEmail:
          type: string
          format: email
          description: Email address the account is trying to switch to.
        expiryTimeStamp:
          type: integer
          format: int64
          description: Unix seconds at which the ticket expires.
      example:
        id: eut_01HZN12345ABC678DEF90GHJKM
        createdAt: '2026-04-22T10:14:00.000Z'
        bigscreenAccountId: acct_01HXJ4Q12345
        token: f1e2d3c4b5a69788776655443322110099887766554433221100ffeeddccbbaa
        newEmail: alice@example.com
        expiryTimeStamp: 1747088000
    LoginToken:
      type: object
      description: |
        Single-use login token minted by `GET /admin/account/{bigscreenAccountId}/token`.
        Wraps `AuthSchemas.SingleUseLoginToken` plus a `message` field. Operators
        hand this token to the account holder to let them sign in without a
        password.
      required:
        - message
        - singleUseLoginToken
      properties:
        message:
          type: string
          example: OK
        singleUseLoginToken:
          type: object
          required:
            - bigscreenAccountId
            - redirectUrl
          properties:
            bigscreenAccountId:
              type: string
            ip:
              type: string
              description: IP captured at token creation time.
            verifyEmailAction:
              type: string
              description: '`AuthSchemas.VerifyEmailAction` — e.g. `VerifyEmailOnConfirm`, `DoNothing`.'
            redirectUrl:
              type: string
              description: Path the user is redirected to after the token is consumed.
      example:
        message: OK
        singleUseLoginToken:
          bigscreenAccountId: acct_01HXJ4Q12345
          ip: ''
          verifyEmailAction: VerifyEmailOnConfirm
          redirectUrl: /accounts
    LoginToken2:
      type: object
      description: |
        Newer-style login token returned by `GET /admin/account/{bigscreenAccountId}/token2`.
        Mirrors `AuthSchemas.LoginToken2`. Carries a public token, an expiry, and a
        short attempts budget so it's safer to email or relay than the v1 format.
      required:
        - publicToken
        - bigscreenAccountId
        - attempts
        - ttlSeconds
      properties:
        id:
          type: string
        createdAt:
          type: string
          format: date-time
        publicToken:
          type: string
          description: 64-char random id the user types (or follows in a link) to log in.
        bigscreenAccountId:
          type: string
        ip:
          type: string
        verifyEmailAction:
          type: string
          description: '`AuthSchemas.VerifyEmailAction` — e.g. `VerifyEmailOnConfirm`, `DoNothing`.'
        redirectUrl:
          type: string
          description: Where the user is redirected after consuming the token.
        attempts:
          type: integer
          description: Remaining redemption attempts. Default 3.
        ttlSeconds:
          type: integer
          description: Time-to-live, in seconds. Default 60.
      example:
        id: lt2_01HZM12345ABC678DEF90GHJKM
        createdAt: '2026-04-22T10:14:00.000Z'
        publicToken: 8k2L3mN4oP5qR6sT7uV8wX9yZ0aB1cD2eF3gH4iJ5kL6mN7oP8qR9sT0uV1wX2y
        bigscreenAccountId: acct_01HXJ4Q12345
        ip: 203.0.113.10
        verifyEmailAction: VerifyEmailOnConfirm
        redirectUrl: /accounts
        attempts: 3
        ttlSeconds: 60
    DeviceStatus:
      type: object
      description: |
        Status record for a hardware device — identifies whether a particular
        device unique identifier has been disabled (Bigscreen-side ban). Returned
        by `GET /admin/device/{deviceUniqueIdentifier}` and updated by the matching
        PUT.
      required:
        - deviceUniqueIdentifier
        - status
      properties:
        deviceUniqueIdentifier:
          type: string
          description: Hardware-derived id (varies per device family).
        status:
          type: string
          description: '`AuthSchemas.AccessStatusCodes` value — `OK`, `Disabled`, etc.'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        notes:
          type: string
          description: Free-form moderator notes about the device.
        history:
          type: array
          items:
            type: object
            additionalProperties: true
          description: Status-change history for the device.
      example:
        deviceUniqueIdentifier: 8b6c2e3f9e54a01b
        status: OK
        createdAt: '2026-04-22T10:14:00.000Z'
        updatedAt: '2026-04-22T10:14:00.000Z'
        notes: ''
        history: []
    IPStatus:
      type: object
      description: |
        Status record for an IP address — identifies whether the IP has been
        flagged or blocked. Returned by `GET /admin/ip/{ip}` and updated by the
        matching PUT. The `ip` URL segment is decoded server-side, so callers must
        URL-encode IPv6 colons before calling.
      required:
        - ip
        - status
      properties:
        ip:
          type: string
          description: IPv4 or IPv6 address.
        status:
          type: string
          description: '`AuthSchemas.AccessStatusCodes` value — `OK`, `Disabled`, etc.'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        notes:
          type: string
          description: Free-form moderator notes.
        history:
          type: array
          items:
            type: object
            additionalProperties: true
      example:
        ip: 203.0.113.10
        status: OK
        createdAt: '2026-04-22T10:14:00.000Z'
        updatedAt: '2026-04-22T10:14:00.000Z'
        notes: ''
        history: []
    AccountReport:
      type: object
      description: |
        A report filed by one Bigscreen account against another, typically with a
        snapshot of the room context the incident happened in. Mirrors
        `Schemas.AccountReport`. The `Admin*` variant returned to admin endpoints
        additionally embeds `targetAccount` and `reporterAccount` `AdminAccount`s.
      required:
        - id
        - reporterBigscreenAccountId
        - targetBigscreenAccountId
        - reason
      properties:
        id:
          type: string
        createdAt:
          type: string
          format: date-time
        reporterBigscreenAccountId:
          type: string
          description: Account that filed the report.
        targetBigscreenAccountId:
          type: string
          description: Account being reported.
        reason:
          type: string
          description: Reporter-supplied reason / category.
        roomId:
          type: string
          description: Room the incident took place in (optional).
        roomName:
          type: string
        roomDescription:
          type: string
        roomAdminUsername:
          type: string
        roomCreatorBigscreenAccountId:
          type: string
        roomType:
          type: string
        roomSnapshot:
          type: object
          additionalProperties: true
          description: Snapshot of the room state at the time of the report.
        targetAccount:
          $ref: '#/components/schemas/AdminAccount'
          description: Embedded `AdminAccount` for the target — present on `/admin/*` responses.
        reporterAccount:
          $ref: '#/components/schemas/AdminAccount'
          description: Embedded `AdminAccount` for the reporter — present on `/admin/*` responses.
      example:
        id: rpt_01HZK12345ABC678DEF90GHJKM
        createdAt: '2026-04-22T10:14:00.000Z'
        reporterBigscreenAccountId: acct_01HXJ4Q12345
        targetBigscreenAccountId: acct_01HXJ4Q67890
        reason: harassment
        roomId: room_alpha
        roomName: Movie Night
        roomDescription: Friday-night feature
        roomAdminUsername: alice
        roomCreatorBigscreenAccountId: acct_01HXJ4Q12345
        roomType: private
        roomSnapshot: {}
    RoomReport:
      type: object
      description: |
        A report filed against a specific room (rather than a specific user).
        Mirrors `Schemas.RoomReport`. The `Admin*` variant returned to admin
        endpoints additionally embeds `targetAccount` (the room's creator) as an
        `AdminAccount`.
      required:
        - id
        - reporterBigscreenAccountId
        - roomId
        - reason
      properties:
        id:
          type: string
        createdAt:
          type: string
          format: date-time
        reporterBigscreenAccountId:
          type: string
          description: Account that filed the report.
        roomId:
          type: string
          description: Room being reported.
        reason:
          type: string
          description: Reporter-supplied reason / category.
        roomName:
          type: string
        roomDescription:
          type: string
        roomAdminUsername:
          type: string
        roomCreatorBigscreenAccountId:
          type: string
        roomType:
          type: string
        roomSnapshot:
          type: object
          additionalProperties: true
          description: Snapshot of the room state at the time of the report.
        targetAccount:
          $ref: '#/components/schemas/AdminAccount'
          description: Embedded `AdminAccount` for the room creator — present on `/admin/*` responses.
      example:
        id: rrpt_01HZL12345ABC678DEF90GHJKM
        createdAt: '2026-04-22T10:14:00.000Z'
        reporterBigscreenAccountId: acct_01HXJ4Q12345
        roomId: room_alpha
        reason: inappropriate-content
        roomName: Movie Night
        roomDescription: Friday-night feature
        roomAdminUsername: alice
        roomCreatorBigscreenAccountId: acct_01HXJ4Q12345
        roomType: private
        roomSnapshot: {}
    ShopifyOrder:
      type: object
      description: |
        A Shopify order — passthrough from the Shopify Admin REST API. We don't
        re-validate the payload; the shape is whatever Shopify returns. This schema
        captures the most-used fields for documentation; consult the
        [Shopify Order resource](https://shopify.dev/docs/api/admin-rest/latest/resources/order)
        for the full spec.

        See `api/src/Shopify.ts` (`ShopifySchemas.ShopifyOrder`) for the cached
        Bigscreen-side mirror.
      required:
        - id
        - name
        - email
        - financial_status
      properties:
        id:
          type: integer
          format: int64
          description: Shopify's numeric order id.
        name:
          type: string
          description: Human-readable order name (e.g. `#1234`).
        email:
          type: string
          format: email
        contact_email:
          type: string
          format: email
        customer:
          type: object
          additionalProperties: true
          description: Shopify customer payload — see Shopify's spec for the full shape.
        financial_status:
          type: string
          description: '`paid`, `pending`, `partially_paid`, `refunded`, `voided`, etc.'
        fulfillment_status:
          type:
            - string
            - 'null'
          description: '`fulfilled`, `partial`, `unfulfilled`, or null.'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        processed_at:
          type: string
          format: date-time
        cancelled_at:
          type:
            - string
            - 'null'
          format: date-time
        closed_at:
          type:
            - string
            - 'null'
          format: date-time
        currency:
          type: string
          example: USD
        presentment_currency:
          type: string
        subtotal_price:
          type: string
        total_price:
          type: string
        total_tax:
          type: string
        total_discounts:
          type: string
        total_weight:
          type: number
        test:
          type: boolean
          description: True for test orders. Required for the `DELETE /admin/shop/shopify_order/{shopifyOrderId}` path.
        tags:
          type: string
          description: Comma-separated tag list.
        note:
          type:
            - string
            - 'null'
        line_items:
          type: array
          items:
            type: object
            additionalProperties: true
            description: Shopify line item — see Shopify's spec.
        fulfillments:
          type: array
          items:
            type: object
            additionalProperties: true
        shipping_address:
          type: object
          additionalProperties: true
        billing_address:
          type: object
          additionalProperties: true
        refunds:
          type: array
          items:
            type: object
            additionalProperties: true
        discount_codes:
          type: array
          items:
            type: object
            additionalProperties: true
        payment_gateway_names:
          type: array
          items:
            type: string
        order_number:
          type: integer
        number:
          type: integer
        token:
          type: string
        cart_token:
          type: string
        checkout_token:
          type: string
        admin_graphql_api_id:
          type: string
        order_status_url:
          type: string
          format: uri
      additionalProperties: true
      example:
        id: 6107853586649
        name: '#10042'
        email: customer@example.com
        financial_status: paid
        fulfillment_status: unfulfilled
        created_at: '2026-04-08T17:42:11.000Z'
        updated_at: '2026-04-08T17:42:11.000Z'
        processed_at: '2026-04-08T17:42:11.000Z'
        currency: USD
        presentment_currency: USD
        subtotal_price: '1199.00'
        total_price: '1199.00'
        total_tax: '0.00'
        total_discounts: '0.00'
        total_weight: 1200
        test: false
        tags: beyond,custom-cushion
        note: null
        line_items: []
        fulfillments: []
        customer:
          id: 7421003628825
          email: customer@example.com
        order_number: 10042
        number: 42
        admin_graphql_api_id: gid://shopify/Order/6107853586649
    BeyondLensOrderPair:
      type: object
      description: |
        Links a Beyond-only Shopify order to a separate Lens-Insert-only Shopify order
        for the same customer. Once paired, the fabricator pipeline treats the two as
        a single shipment.

        Created via `POST /admin/shop/shopify_orders/pair`. The pairing is created from
        order **names** (`#1234`), not ids — both orders must belong to the same
        Shopify customer and have the expected line-item composition.
      required:
        - id
        - beyondShopifyOrderId
        - lensOnlyShopifyOrderId
        - beyondShopifyOrderName
        - lensOnlyShopifyOrderName
      properties:
        id:
          type: string
          description: Pair record id.
        createdAt:
          type: string
          format: date-time
        beyondShopifyOrderId:
          type: string
          description: Numeric Shopify id of the Beyond-only order.
        beyondShopifyOrderName:
          type: string
          description: Shopify name of the Beyond-only order (e.g. `#1234`).
        lensOnlyShopifyOrderId:
          type: string
          description: Numeric Shopify id of the Lens-Insert-only order.
        lensOnlyShopifyOrderName:
          type: string
          description: Shopify name of the Lens-Insert-only order.
        shopifyCustomerId:
          type: string
          description: Shopify customer id shared by both orders.
        creatorBigscreenAccountId:
          type: string
          description: Account that created the pair record.
      example:
        id: 01HZA3M5K8XYZ7P9QRSTUVWXYZ
        createdAt: '2026-04-22T10:14:00.000Z'
        beyondShopifyOrderId: '6107853586649'
        beyondShopifyOrderName: '#10042'
        lensOnlyShopifyOrderId: '6107853659483'
        lensOnlyShopifyOrderName: '#10043'
        shopifyCustomerId: '7421003628825'
        creatorBigscreenAccountId: acct_01HXJ4Q12345
    BigOrderShippingSummary:
      type: object
      description: |
        Computed shipping layout for a Big Order — the line-item-to-shipment-group
        mapping, the current shipment per group (if any), and the available
        inventory slots for groups that haven't shipped yet. Returned by
        `GET /admin/shop/order/{bigOrderId}/shipping_summary` and rendered on the
        Arda Big Order detail page.
      properties:
        lineItemSummary:
          type: object
          description: Per-line-item layout details.
          properties:
            lineItems:
              type: array
              description: Includes fulfilled, unfulfilled, and refunded items.
              items:
                type: object
                properties:
                  shopifyOrderLineItemId:
                    type: string
                  shipmentGroupId:
                    type: string
                    description: Empty when the item is not yet grouped.
                  type:
                    description: Product taxonomy.
                    $ref: '#/components/schemas/BigProductType'
                  fulfilled:
                    type: boolean
                  refunded:
                    type: boolean
                additionalProperties: true
          additionalProperties: true
        shipmentGroups:
          type: object
          description: |
            Shipment-group id → group details. Each group either has a `shipment`
            (already created) or `inventorySlots` (waiting to be picked).
          additionalProperties:
            type: object
            properties:
              shipment:
                $ref: '#/components/schemas/BigShipment'
              inventorySlots:
                type: array
                items:
                  type: object
                  additionalProperties: true
                  description: Available inventory slot for this group.
      example:
        lineItemSummary:
          lineItems:
            - shopifyOrderLineItemId: '13594621186073'
              shipmentGroupId: default
              type: BigscreenBeyondV1
              fulfilled: false
              refunded: false
            - shopifyOrderLineItemId: '13594621317145'
              shipmentGroupId: default
              type: BeyondCushionV1
              fulfilled: false
              refunded: false
        shipmentGroups:
          default:
            inventorySlots:
              - productType: BigscreenBeyondV1
                fulfilled: false
              - productType: BeyondCushionV1
                fulfilled: false
    BigOrderChecklistResult:
      type: object
      description: |
        Result of running the Big Order checklist — what action the state machine
        thinks should happen next. Returned by `GET /admin/shop/order/{bigOrderId}/checklist`,
        embedded on `BigOrder.checklistResult`, and surfaced inside `runBigOrderChecklistAll`'s
        summary.

        `success: false` is **not** an API failure — it means the checklist ran
        cleanly but the order isn't ready to advance. Callers that treat any
        non-success as fatal will mishandle this.
      required:
        - success
      properties:
        success:
          type: boolean
        nextAction:
          type: string
          description: |
            Suggested next action. Values are from `FabricatorSchemas.BigOrderAction`
            (e.g. `SendScanRequest`, `AdminOverrideIPD`, `CreateJob`, `CreateShipment`,
            `ShipIt`, `Complete`, `Unknown`).
        message:
          type: string
          description: 'Human-readable explanation, especially when `success: false`.'
        metaData:
          description: Optional action-specific structured payload.
      example:
        success: true
        nextAction: CreateJob
        message: Scan request verified; ready to spawn job.
        metaData:
          scanRequestId: scan_01HXJ58DQX5N9P2VRC8B3MZ7Y4
    BigOrderSyncResult:
      type: object
      description: |
        Result of a per-Big-Order sync (`GET /admin/shop/order/{bigOrderId}/sync` or
        the line-items-only variant). Reports what changed, plus any non-fatal errors
        surfaced during the sync.
      properties:
        bigOrderId:
          type: string
        shopifyOrderId:
          type: string
        syncedAt:
          type: string
          format: date-time
        lineItemsChanged:
          type: integer
          description: Number of line items added, updated, or removed.
        snapshotsRefreshed:
          type: integer
          description: Number of line-item snapshot records rewritten.
        shipmentGroupsRepaired:
          type: integer
          description: Shipment-group fixups applied.
        bigOrder:
          $ref: '#/components/schemas/BigOrder'
          description: Updated order, when the sync changed any persisted fields.
        errors:
          type: array
          items:
            type: string
          description: Non-fatal errors surfaced during the sync.
      example:
        bigOrderId: 01HXJ4S3M0Q3TRBQ3JV9P5W2VC
        shopifyOrderId: '6107853586649'
        syncedAt: '2026-04-22T10:14:00.000Z'
        lineItemsChanged: 2
        snapshotsRefreshed: 4
        shipmentGroupsRepaired: 0
        errors: []
