openapi: 3.1.0
info:
  title: Shotmatic External API
  version: 1.0.0-draft
  summary: Public API for scripted video creation, clip extraction, and workflow execution.
  description: |
    Proposed external API for Shotmatic.

    This contract intentionally differs from the current internal `app/api/*` routes.
    The public surface is resource-oriented, provider-blind, versioned under `/v1`,
    and designed to support three creation modes:

    1. Interactive creation through `video-plans`
       - create a draft plan
       - review and edit script, scenes, voice, and render options
       - approve the plan to start rendering

    2. Fully automated creation through `videos`
       - submit a one-shot request
       - let the system plan and render without a human approval step

    3. Reusable headless creation through `workflow-profiles`
       - author a reusable workflow profile from a long description or explicit blocks
       - bind team, brand, or presenter defaults through workflow-profile bindings
       - run each job through `sessions` with `source.kind=workflow_profile` or `source.kind=workflow_profile_binding`

    Voice identifiers are canonical entity IDs and do not expose whether the
    underlying TTS engine is Fish Audio, ElevenLabs, or another provider.

    When `webhook_secret` is provided on video creation or plan approval,
    Shotmatic signs outbound webhook deliveries with:
    - `X-Shotmatic-Timestamp`
    - `X-Shotmatic-Signature`

    Consumers should verify `v1=<hex_hmac_sha256>` over
    `${timestamp}.${raw_json_body}` and reject timestamps older than 5 minutes.

    This API is also the intended substrate for a future MCP server. MCP tools
    should map onto these resources instead of calling UI-shaped internal routes.
servers:
  - url: https://api.shotmatic.com/v1
    description: Production
  - url: https://shotvibes-m6c94ci8t-sofabrain.vercel.app/v1
    description: Protected OM9 sandbox (Vercel SSO and scoped API key)
  - url: https://staging-api.shotmatic.com/v1
    description: Staging
  - url: http://localhost:3000/v1
    description: Local development
security:
  - bearerAuth: []
tags:
  - name: Capabilities
  - name: API Keys
  - name: Voice Catalog
    description: Browse provider-neutral retained Voice choices without live provider calls.
  - name: Soundtracks
    description: Browse stable soundtrack choices without a live provider call.
  - name: Voices
  - name: Media
  - name: Workflow Node Types
  - name: Workflow Profiles
  - name: Workflow Profile Bindings
  - name: Presenter Profiles
  - name: Avatar Identities
    description: Manage provider-neutral avatar identity consent for the authenticated account.
  - name: Avatar Profiles
  - name: Avatar Source Packs
  - name: Video Plans
  - name: Videos
  - name: Render Runs
  - name: Clip Tasks
  - name: Workflows
  - name: Webhooks
  - name: Usage
paths:
  /avatar-identities/{identityId}/consent:
    get:
      tags:
        - Avatar Identities
      operationId: getAvatarIdentityConsent
      summary: Read the latest consent state for one account-owned Avatar Identity
      description: Returns only the safe consent projection. Evidence references and hashes remain private service data.
      parameters:
        - in: path
          name: identityId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Avatar Identity consent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarIdentityConsent'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /avatar-identities/{identityId}/consent/revoke:
    post:
      tags:
        - Avatar Identities
      operationId: revokeAvatarIdentityConsent
      summary: Revoke one exact account-owned Avatar Identity consent grant
      description: Preserves immutable evidence, disables the Identity, Profiles, and bindings, and releases only unclaimed paid work. In-flight work remains quarantined by the normal reconciliation rules.
      parameters:
        - in: path
          name: identityId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RevokeAvatarIdentityConsentRequest'
      responses:
        '200':
          description: Revoked consent projection or idempotent replay
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarIdentityConsent'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
  /avatar-profiles:
    get:
      tags:
        - Avatar Profiles
      operationId: listAvatarProfiles
      summary: Evaluate Avatar Profiles owned by the API client account
      description: Returns provider-neutral quick-selection resources with request-specific cached, billable, setup, consent, compatibility, or provider-availability state. Evaluation and selection do not call a provider. Provider bindings and vendor resource IDs are never exposed.
      parameters:
        - $ref: '#/components/parameters/AvatarPickerStrategy'
        - $ref: '#/components/parameters/AvatarPickerAspectRatio'
        - $ref: '#/components/parameters/AvatarPickerDurationSeconds'
        - $ref: '#/components/parameters/AvatarPickerLocale'
        - $ref: '#/components/parameters/AvatarPickerOutputBackground'
        - $ref: '#/components/parameters/AvatarPickerAudioSha256'
      responses:
        '200':
          description: Request-aware Avatar Profiles
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarProfileCollection'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /avatar-profiles/{profileId}:
    get:
      tags:
        - Avatar Profiles
      operationId: getAvatarProfile
      summary: Read one Avatar Profile owned by the API client account
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/AvatarPickerStrategy'
        - $ref: '#/components/parameters/AvatarPickerAspectRatio'
        - $ref: '#/components/parameters/AvatarPickerDurationSeconds'
        - $ref: '#/components/parameters/AvatarPickerLocale'
        - $ref: '#/components/parameters/AvatarPickerOutputBackground'
        - $ref: '#/components/parameters/AvatarPickerAudioSha256'
      responses:
        '200':
          description: Avatar Profile
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarProfile'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Avatar Profiles
      operationId: assignAvatarProfileVoice
      summary: Assign one ready Account Voice to an Avatar Profile
      description: Atomically verifies same-account Profile ownership, a ready Account Voice, and one compatible service-only Voice binding, then returns recomputed provider-free Profile availability. It never calls a provider.
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/AvatarPickerStrategy'
        - $ref: '#/components/parameters/AvatarPickerAspectRatio'
        - $ref: '#/components/parameters/AvatarPickerDurationSeconds'
        - $ref: '#/components/parameters/AvatarPickerLocale'
        - $ref: '#/components/parameters/AvatarPickerOutputBackground'
        - $ref: '#/components/parameters/AvatarPickerAudioSha256'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AvatarProfileVoiceAssignmentRequest'
      responses:
        '200':
          description: Avatar Profile with recomputed availability
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarProfile'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
  /avatar-profiles/{profileId}/motion-bases:
    get:
      tags:
        - Avatar Profiles
      operationId: listAvatarMotionBases
      summary: List qualified Motion Bases for one account-owned Avatar Profile
      description: Returns immutable provider-neutral base-motion videos for the Profile Identity. Reading this collection never calls a provider.
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Qualified Motion Bases
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarMotionBaseCollection'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags:
        - Avatar Profiles
      operationId: createAvatarMotionBase
      summary: Register a selected Motion Reference as a reusable Avatar Motion Base
      description: The selected Source Asset must be a private-R2 MP4 motion_reference from the same qualified Source Pack, Identity, and Look, with matching SHA/duration evidence and active avatar-generation consent. Arbitrary media and provider resource IDs are not accepted.
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAvatarMotionBaseRequest'
      responses:
        '201':
          description: Registered or safely replayed Motion Base
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarMotionBase'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          description: Source lineage, private-R2 evidence, Identity/Look relationship, or active consent is invalid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /avatar-profiles/{profileId}/motion-bases/{motionBaseId}:
    get:
      tags:
        - Avatar Profiles
      operationId: getAvatarMotionBase
      summary: Read one qualified account-owned Avatar Motion Base
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: motionBaseId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Provider-neutral Motion Base
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarMotionBase'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /avatar-profiles/{profileId}/motion-bases/{motionBaseId}/lip-sync-quotes:
    post:
      tags:
        - Avatar Profiles
      operationId: quoteAvatarLipSync
      summary: Reuse or quote a lip-synced Performance from one Motion Base
      description: |
        Checks the account-owned private-R2 derivative cache before any adapter estimate.
        A miss freezes one provider-neutral lip-sync command, adapter capability snapshot,
        estimate, product price, and provider idempotency key. Quoting never submits work
        to a provider. The selected Motion Base and audio must remain immutable, qualified,
        private-R2 assets owned by the same account.
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: motionBaseId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAvatarLipSyncQuoteRequest'
      responses:
        '200':
          description: Reused ready lip-sync derivative; no estimate, approval, or provider call occurred
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarLipSyncJob'
        '201':
          description: Frozen lip-sync quote awaiting exact approval
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarLipSyncQuote'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Motion Base, consent, or immutable media evidence changed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          $ref: '#/components/responses/ValidationError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /avatar-profiles/{profileId}/motion-bases/{motionBaseId}/lip-sync-quotes/{quoteId}:
    get:
      tags:
        - Avatar Profiles
      operationId: getAvatarLipSyncQuote
      summary: Read one safe account-owned lip-sync quote
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: motionBaseId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: quoteId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Safe provider-neutral quote state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarLipSyncQuote'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /avatar-profiles/{profileId}/motion-bases/{motionBaseId}/lip-sync-quotes/{quoteId}/approve:
    post:
      tags:
        - Avatar Profiles
      operationId: approveAvatarLipSyncQuote
      summary: Approve the exact lip-sync quote and queue durable work
      description: Atomically verifies amount and pricing version, enforces account/API-key spend limits, reserves credits, and queues the frozen worker command. It never calls the provider inline; ambiguous acceptance is quarantined and reconciled without provider failover.
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: motionBaseId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: quoteId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApproveAvatarLipSyncQuoteRequest'
      responses:
        '202':
          description: Credits reserved and the frozen lip-sync job queued or reused
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarLipSyncQuote'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: Insufficient credits or API-key spend cap exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
  /avatar-profiles/{profileId}/motion-bases/{motionBaseId}/lip-sync-jobs/{jobId}:
    get:
      tags:
        - Avatar Profiles
      operationId: getAvatarLipSyncJob
      summary: Poll one durable account-owned lip-sync job
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: motionBaseId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: jobId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Safe provider-neutral job state and adopted private-R2 result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarLipSyncJob'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /avatar-profiles/{profileId}/provider-consent:
    get:
      tags:
        - Avatar Profiles
      operationId: getAvatarProviderConsent
      summary: Read the provider approval state for one account-owned Digital Twin
      description: Returns only Shotmatic's safe consent projection. Provider names, group IDs, resource IDs, and evidence references remain service-only.
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Durable provider-consent state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarProviderConsent'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
        - Avatar Profiles
      operationId: beginAvatarProviderConsent
      summary: Prepare the browser approval flow for one account-owned Digital Twin
      description: Validates immutable local rights evidence, creates or reuses a zero-cost idempotent provider-consent operation, and returns a short-lived browser URL. It never performs avatar or video generation.
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
      responses:
        '200':
          description: Provider consent is already approved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarProviderConsent'
        '202':
          description: Browser approval is required or being reconciled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarProviderConsent'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /avatar-profiles/{profileId}/performances:
    post:
      tags:
        - Avatar Profiles
      operationId: createAvatarPerformance
      summary: Resolve or create one reusable Avatar Performance
      description: |
        Uses the selected account-owned Avatar Profile and immutable private-R2 audio.
        The direct endpoint reuses an existing Performance or permits a zero-cost fixture only.
        It never accepts provider IDs, URLs, presentation layout, or a client-supplied spend cap.
        A paid cache miss requires Shotmatic's separate quote and approval workflow.
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAvatarPerformanceRequest'
      responses:
        '200':
          description: Reused ready Avatar Performance
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarPerformance'
        '201':
          description: Newly completed zero-cost Avatar Performance
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarPerformance'
        '202':
          description: Avatar Performance is processing
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarPerformance'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: A paid Performance quote and approval is required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationError'
  /avatar-profiles/{profileId}/performances/{performanceId}:
    get:
      tags:
        - Avatar Profiles
      operationId: getAvatarPerformance
      summary: Poll one account-owned Avatar Performance
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: performanceId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Avatar Performance state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarPerformance'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /avatar-profiles/{profileId}/performance-quotes:
    post:
      tags:
        - Avatar Profiles
      operationId: quoteAvatarPerformance
      summary: Quote one account-owned Avatar Performance cache miss
      description: |
        Checks the owned Performance cache before requesting any provider estimate. A cache hit
        returns the ready Avatar Performance. A miss freezes the exact Profile binding, immutable
        private-R2 audio, provider-neutral command, adapter version, estimate, product price, and
        provider idempotency key. Quoting never invokes a provider mutation.
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAvatarPerformanceRequest'
      responses:
        '200':
          description: Existing ready Avatar Performance; no approval or provider estimate required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarPerformance'
        '201':
          description: Frozen Avatar Performance quote awaiting exact approval
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarPerformanceQuote'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /avatar-profiles/{profileId}/performance-quotes/{quoteId}:
    get:
      tags:
        - Avatar Profiles
      operationId: getAvatarPerformanceQuote
      summary: Read one account-owned Avatar Performance quote
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: quoteId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Safe quote projection
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarPerformanceQuote'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /avatar-profiles/{profileId}/performance-quotes/{quoteId}/approve:
    post:
      tags:
        - Avatar Profiles
      operationId: approveAvatarPerformanceQuote
      summary: Approve an exact Avatar Performance quote and queue durable work
      description: Atomically verifies the quoted amount and pricing version, enforces the API-key spend cap, reserves credits, and queues the frozen worker job. It does not call the provider inline.
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: quoteId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApproveAvatarPerformanceQuoteRequest'
      responses:
        '202':
          description: Quote approved and frozen Performance queued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarPerformanceQuote'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: Insufficient credits or API-key spend cap exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
  /avatar-profiles/{profileId}/performances/{performanceId}/matting-quotes:
    post:
      tags:
        - Avatar Profiles
      operationId: quoteAvatarVideoMatting
      summary: Reuse or quote a decoded-alpha cutout for one opaque Performance
      description: |
        Checks the account-owned decoded-alpha private-R2 cache before any adapter estimate.
        A miss freezes one provider-neutral VideoMatting command, adapter version, estimate,
        product price, and provider idempotency key. Quoting never calls a provider. Native
        verified alpha and qualified chroma should be preferred before using this fallback.
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: performanceId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAvatarVideoMattingQuoteRequest'
      responses:
        '200':
          description: Reused decoded-alpha derivative; no estimate, approval, or provider call occurred
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarMattingDerivative'
        '201':
          description: Frozen VideoMatting quote awaiting exact approval
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarVideoMattingQuote'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: The Performance changed, is not ready, or already has decoded alpha
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          $ref: '#/components/responses/ValidationError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /avatar-profiles/{profileId}/performances/{performanceId}/matting-quotes/{quoteId}:
    get:
      tags:
        - Avatar Profiles
      operationId: getAvatarVideoMattingQuote
      summary: Read one safe account-owned VideoMatting quote
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: performanceId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: quoteId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Safe provider-neutral quote state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarVideoMattingQuote'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /avatar-profiles/{profileId}/performances/{performanceId}/matting-quotes/{quoteId}/approve:
    post:
      tags:
        - Avatar Profiles
      operationId: approveAvatarVideoMattingQuote
      summary: Approve the exact VideoMatting quote and queue durable work
      description: Atomically verifies amount and pricing version, enforces account/API-key spend limits, reserves credits, and queues the frozen worker command. It never calls the provider inline; ambiguous acceptance is reconciled without failover.
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: performanceId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: quoteId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApproveAvatarVideoMattingQuoteRequest'
      responses:
        '202':
          description: Credits reserved and the frozen VideoMatting job queued or reused
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarVideoMattingQuote'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: Insufficient credits or API-key spend cap exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
  /avatar-profiles/{profileId}/performances/{performanceId}/matting-jobs/{jobId}:
    get:
      tags:
        - Avatar Profiles
      operationId: getAvatarVideoMattingJob
      summary: Poll one durable account-owned VideoMatting job
      parameters:
        - in: path
          name: profileId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: performanceId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: jobId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Safe provider-neutral job state and decoded-alpha result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarVideoMattingJob'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /avatar-source-packs:
    get:
      tags:
        - Avatar Source Packs
      operationId: listAvatarSourcePacks
      summary: List account-owned avatar Source Packs
      responses:
        '200':
          description: Avatar Source Packs
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarSourcePackCollection'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags:
        - Avatar Source Packs
      operationId: createAvatarSourcePack
      summary: Atomically create an avatar identity and draft Source Pack
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAvatarSourcePackRequest'
      responses:
        '201':
          description: Draft Source Pack
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarSourcePack'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          description: Idempotency conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /avatar-source-packs/digital-twins:
    post:
      tags:
        - Avatar Source Packs
      operationId: createAvatarDigitalTwinSourcePack
      summary: Atomically record one Digital Twin training source and separate consent evidence
      description: Accepts two different completed account-owned private-R2 video media IDs, freezes their SHA-256 evidence, creates one trained identity and Source Pack, and remains provider-off. Qualification and Profile provisioning are separate operations; provider mutation requires an exact quote and explicit approval.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAvatarDigitalTwinSourcePackRequest'
      responses:
        '201':
          description: Trained-twin Source Pack with one immutable training-video primary
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarSourcePack'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: One of the account-owned private-R2 media assets was not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          description: Rights, consent, storage, or immutable hash evidence is incomplete
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /avatar-source-packs/{sourcePackId}:
    get:
      tags:
        - Avatar Source Packs
      operationId: getAvatarSourcePack
      summary: Read one account-owned avatar Source Pack
      parameters:
        - in: path
          name: sourcePackId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Avatar Source Pack
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarSourcePack'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /avatar-source-packs/{sourcePackId}/candidates:
    parameters:
      - in: path
        name: sourcePackId
        required: true
        schema:
          type: string
          format: uuid
    get:
      tags:
        - Avatar Source Packs
      operationId: listAvatarSourceCandidates
      summary: List non-expired private-R2 candidates for a Source Pack
      responses:
        '200':
          description: Avatar source candidates
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarSourceCandidateCollection'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
        - Avatar Source Packs
      operationId: addAvatarSourceCandidate
      summary: Register an owned, hashed private-R2 image as a temporary candidate
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AddAvatarSourceCandidateRequest'
      responses:
        '201':
          description: Candidate registered or replayed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarSourceCandidate'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /avatar-source-packs/{sourcePackId}/primary:
    post:
      tags:
        - Avatar Source Packs
      operationId: selectAvatarSourcePackPrimary
      summary: Deliberately promote one candidate into immutable primary membership
      parameters:
        - in: path
          name: sourcePackId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SelectAvatarSourcePrimaryRequest'
      responses:
        '200':
          description: Source Pack with its selected identity anchor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarSourcePack'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Selection or idempotency conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /avatar-source-packs/{sourcePackId}/qualification:
    parameters:
      - in: path
        name: sourcePackId
        required: true
        schema:
          type: string
          format: uuid
    get:
      tags:
        - Avatar Source Packs
      operationId: getLatestAvatarSourceQualification
      summary: Poll the latest safe Source Pack qualification state
      responses:
        '200':
          description: Provider-free qualification state and report
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarSourceQualification'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
        - Avatar Source Packs
      operationId: qualifyAvatarSourcePack
      summary: Queue free private-R2 validation of the deliberately selected source set
      description: Reads and hashes only the frozen private-R2 Source Pack assets. It makes zero provider calls, reserves no credits, and never replaces the selected identity anchor.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AvatarSourceQualificationRequest'
      responses:
        '202':
          description: Qualification queued or replayed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarSourceQualification'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Source Pack has no immutable primary or is in an incompatible state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Active avatar generation consent is missing or expired
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /avatar-source-packs/{sourcePackId}/profile-provisioning:
    parameters:
      - in: path
        name: sourcePackId
        required: true
        schema:
          type: string
          format: uuid
    get:
      tags:
        - Avatar Source Packs
      operationId: getLatestAvatarProfileProvisioning
      summary: Poll the latest Profile provisioning job for a Source Pack
      responses:
        '200':
          description: Safe provider-neutral Profile provisioning state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarProfileProvisioningJob'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
        - Avatar Source Packs
      operationId: provisionAvatarProfile
      summary: Provision a reusable Avatar Profile from a qualified Source Pack
      description: Shotmatic selects and freezes a capability adapter internally. This convenience endpoint queues only a zero-cost cached or fixture adapter. When a costed adapter is required, clients create and explicitly approve a Profile provisioning quote. Retries reuse the same provider idempotency key and ambiguous submissions are reconciled without provider fallback.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AvatarProfileProvisioningRequest'
      responses:
        '202':
          description: Profile provisioning queued or replayed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarProfileProvisioningJob'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: A paid adapter is available but requires a separate exact quote and explicit approval
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: The Source Pack is not qualified or its source fingerprint changed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Active avatar consent is missing or expired
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: No zero-cost identity-provisioning capability adapter is enabled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /avatar-source-packs/{sourcePackId}/profile-provisioning-quotes:
    get:
      tags:
        - Avatar Source Packs
      operationId: getLatestAvatarProfileProvisioningQuote
      summary: Read the latest safe account-owned Profile provisioning quote
      description: Returns the latest provider-neutral quote and its frozen public request so an interrupted Studio flow can resume without exposing adapter or vendor state.
      parameters:
        - in: path
          name: sourcePackId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Latest safe Profile provisioning quote
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarProfileProvisioningQuote'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
        - Avatar Source Packs
      operationId: createAvatarProfileProvisioningQuote
      summary: Quote reusable Avatar Profile creation without invoking a provider mutation
      description: Reuses an unexpired body-bound quote before estimating again. Shotmatic freezes the qualified source fingerprint, provider adapter, provider estimate, product price, and provider idempotency key while keeping vendor details private.
      parameters:
        - in: path
          name: sourcePackId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AvatarProfileProvisioningRequest'
      responses:
        '201':
          description: New or reused body-bound Profile provisioning quote
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarProfileProvisioningQuote'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: The Source Pack is not qualified or its immutable primary changed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Consent is missing or the estimate exceeds the server-side provider cost cap
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          description: No identity-provisioning capability adapter is enabled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /avatar-source-packs/{sourcePackId}/profile-provisioning-quotes/{quoteId}:
    get:
      tags:
        - Avatar Source Packs
      operationId: getAvatarProfileProvisioningQuote
      summary: Read one safe account-owned Profile provisioning quote
      parameters:
        - in: path
          name: sourcePackId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: quoteId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Safe quote state with no provider cost or vendor identifiers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarProfileProvisioningQuote'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /avatar-source-packs/{sourcePackId}/profile-provisioning-quotes/{quoteId}/approve:
    post:
      tags:
        - Avatar Source Packs
      operationId: approveAvatarProfileProvisioningQuote
      summary: Reserve credits and queue the exact frozen Profile creation job
      description: Atomically checks the expected amount and pricing version, enforces account balance and API-key daily spend caps, reserves credits, and creates or reuses the frozen provider job. The provider request remains asynchronous and is never submitted inline.
      parameters:
        - in: path
          name: sourcePackId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: quoteId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AvatarProfileProvisioningApprovalRequest'
      responses:
        '202':
          description: Credits reserved and the durable Profile provisioning job queued or replayed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarProfileProvisioningQuote'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: Insufficient credits or API-key daily spend cap exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Quote expired, changed, or is not approvable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /avatar-source-packs/{sourcePackId}/acquisition-quotes:
    get:
      tags:
        - Avatar Source Packs
      operationId: getLatestAvatarSourceAcquisitionQuote
      summary: Read the latest safe account-owned Source acquisition quote
      description: Returns the latest provider-neutral quote and its frozen public request so an interrupted Studio flow can resume without exposing adapter or vendor state.
      parameters:
        - in: path
          name: sourcePackId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Latest safe Source acquisition quote
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarSourceAcquisitionQuote'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags:
        - Avatar Source Packs
      operationId: createAvatarSourceAcquisitionQuote
      summary: Quote a provider-neutral Source Pack candidate acquisition
      description: Reuses an existing body-bound quote before consulting an adapter. Provider selection, model names, and raw provider estimates are never exposed.
      parameters:
        - in: path
          name: sourcePackId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AvatarSourceAcquisitionQuoteRequest'
      responses:
        '201':
          description: New or reused body-bound quote
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarSourceAcquisitionQuote'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          description: No configured provider capability or the estimate exceeds the server-side spend boundary
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /avatar-source-packs/{sourcePackId}/acquisition-quotes/{quoteId}:
    get:
      tags:
        - Avatar Source Packs
      operationId: getAvatarSourceAcquisitionQuote
      summary: Read the safe state of one account-owned acquisition quote
      parameters:
        - in: path
          name: sourcePackId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: quoteId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Safe quote state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarSourceAcquisitionQuote'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /avatar-source-packs/{sourcePackId}/acquisition-quotes/{quoteId}/approve:
    post:
      tags:
        - Avatar Source Packs
      operationId: approveAvatarSourceAcquisitionQuote
      summary: Reserve credits against the exact quote before provider submission
      description: Atomically checks the expected amount and pricing version, enforces API-key daily spend caps, and reserves credits. Approval never submits a provider request inline.
      parameters:
        - in: path
          name: sourcePackId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: quoteId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AvatarSourceAcquisitionApprovalRequest'
      responses:
        '202':
          description: Credits reserved; durable provider dispatch is pending or already in progress
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarSourceAcquisitionQuote'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: Insufficient credits or API-key daily spend cap exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Quote expired, changed, or is not approvable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /avatar-source-packs/{sourcePackId}/studio-state:
    get:
      tags:
        - Avatar Source Packs
      operationId: getAvatarStudioResumeState
      summary: Resume an account-owned Avatar Studio flow from durable state
      description: Aggregates only provider-neutral Source Pack, candidate, qualification, quote, and Profile job projections. This read never invokes a provider and never exposes storage keys, adapter choices, vendor identifiers, or provider costs.
      parameters:
        - in: path
          name: sourcePackId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Durable provider-neutral Avatar Studio state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarStudioResumeState'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /media/imports:
    post:
      tags:
        - Media
      operationId: createOrderedMediaImport
      summary: Import one durable ordered-media source asynchronously
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderedMediaImportRequestV1'
      responses:
        '202':
          description: Import queued or replayed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderedMediaImportV1'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          description: Idempotency or producer-asset conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          $ref: '#/components/responses/ValidationError'
        '503':
          description: Ordered-media mutations are disabled for this principal or environment
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /media/imports/{importId}:
    get:
      tags:
        - Media
      operationId: getOrderedMediaImport
      summary: Read one ordered-media import
      parameters:
        - in: path
          name: importId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Import state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderedMediaImportV1'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /media/imports/{importId}/retry:
    post:
      tags:
        - Media
      operationId: retryOrderedMediaImport
      summary: Retry one retryable ordered-media import with a refreshed URL
      parameters:
        - in: path
          name: importId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderedMediaImportRetryRequestV1'
      responses:
        '202':
          description: Import retry queued or replayed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderedMediaImportV1'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Import is not retryable or the idempotency body conflicts
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /sessions:
    get:
      tags:
        - Video Plans
      operationId: listSessions
      summary: List account-owned creation sessions
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/PageSize'
        - in: query
          name: status
          schema:
            $ref: '#/components/schemas/VideoPlanStatus'
        - in: query
          name: project_id
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Creation sessions
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/VideoPlan'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
                required:
                  - data
                  - pagination
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags:
        - Video Plans
      operationId: createSession
      summary: Create a draft-first Shotmatic session
      description: |
        Canonical creation entry point for regular videos, Avatar videos, workflow-profile runs,
        clips, captions, and deterministic `ordered_media.v1` composites.

        Script requests are prepared locally into a reviewable draft without a provider call. Use
        account-owned `input_assets` plus `asset_bindings` for exact scene order. Avatar requests use
        `creative.video_type = avatar_video`, an account Avatar Profile ID, and either a concise
        `presentation_preset` or exact `avatar_presentation_request.v2`; the review response always
        returns the resolved exact presentation. Poll until `ready_for_review`, inspect the estimate,
        then approve with the reviewed session version and a caller-selected credit cap. Creating and
        quoting do not submit Avatar, TTS, image, or lip-sync provider work.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/CreateVideoPlanRequest'
                - $ref: '#/components/schemas/OrderedMediaSessionRequestV1'
      responses:
        '202':
          description: Session persisted or deterministic ordered-media draft accepted
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/VideoPlan'
                  - $ref: '#/components/schemas/OrderedMediaSessionV1'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          description: Revision, media readiness, or idempotency conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          $ref: '#/components/responses/ValidationError'
        '503':
          description: Ordered-media mutations are disabled for this principal or environment
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /sessions/{sessionId}:
    get:
      tags:
        - Video Plans
      operationId: getSession
      summary: Read one account-owned creation session
      parameters:
        - in: path
          name: sessionId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Current session state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VideoPlan'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Video Plans
      operationId: updateSession
      summary: Update a reviewable creation session
      parameters:
        - in: path
          name: sessionId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateVideoPlanRequest'
      responses:
        '200':
          description: Updated session
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VideoPlan'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
  /sessions/{sessionId}/approve:
    post:
      tags:
        - Video Plans
      operationId: approveSession
      summary: Freeze reviewed artifacts, reserve credits, and queue one render
      description: |
        Regular and Avatar sessions use `ApproveVideoPlanRequest`. Avatar approval must include the
        exact current `avatar_performance_batch` authorization. Shotmatic reserves the parent amount
        and queues the normal workflow; provider mutation is never performed inline with approval.
        Bind approval to `expected_session_version` and `max_credits`; a mismatch fails before the
        render queue. Deterministic ordered-media sessions use `OrderedMediaApprovalRequestV1`.
      parameters:
        - in: path
          name: sessionId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/ApproveVideoPlanRequest'
                - $ref: '#/components/schemas/OrderedMediaApprovalRequestV1'
      responses:
        '202':
          description: Render queued with one reservation
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/VideoPlan'
                  - $ref: '#/components/schemas/OrderedMediaApprovalV1'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Reviewed version, estimate, reservation, or idempotency conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /sessions/{sessionId}/preview:
    get:
      tags:
        - Video Plans
      operationId: getSessionPreview
      summary: Read the review surface for one creation session
      parameters:
        - in: path
          name: sessionId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Session preview bundle
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /sessions/{sessionId}/artifacts:
    get:
      tags:
        - Render Runs
      operationId: listSessionArtifacts
      summary: List immutable descriptor, timeline, props, and Avatar Track artifacts
      parameters:
        - in: path
          name: sessionId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Session artifacts
          content:
            application/json:
              schema:
                type: object
                properties:
                  object:
                    const: list
                  data:
                    type: array
                    items:
                      type: object
                      additionalProperties: true
                required:
                  - object
                  - data
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /sessions/{sessionId}/render-run/latest:
    get:
      tags:
        - Render Runs
      operationId: getLatestSessionRenderRun
      summary: Read the latest render run attached to a session
      parameters:
        - in: path
          name: sessionId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Latest render run
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /sessions/{sessionId}/avatar-performance-batches:
    post:
      tags:
        - Video Plans
      operationId: quoteAvatarSessionPerformanceBatch
      summary: Quote only the unique Avatar speaking allocations in the current session revision
      description: Performs local planning estimates and cache-aware allocation only. It does not submit TTS, avatar, image, or lip-sync provider work.
      parameters:
        - in: path
          name: sessionId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '201':
          description: Frozen session-level Avatar Performance batch awaiting exact approval
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarSessionPerformanceBatch'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
  /sessions/{sessionId}/avatar-performance-batches/{batchId}:
    get:
      tags:
        - Video Plans
      operationId: getAvatarSessionPerformanceBatch
      summary: Read one account-owned Avatar session Performance batch
      parameters:
        - in: path
          name: sessionId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: batchId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Safe batch state and pricing projection
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarSessionPerformanceBatch'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /sessions/{sessionId}/avatar-performance-batches/{batchId}/approve:
    post:
      tags:
        - Video Plans
      operationId: approveAvatarSessionPerformanceBatch
      summary: Reserve the exact session-level Avatar batch without submitting provider work inline
      parameters:
        - in: path
          name: sessionId
          required: true
          schema:
            type: string
            format: uuid
        - in: path
          name: batchId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApproveAvatarSessionBatchRequest'
      responses:
        '200':
          description: Exact batch approved and parent credits reserved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AvatarSessionPerformanceBatch'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: Insufficient credits or API-key spend cap exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
  /sessions/{sessionId}/branch:
    post:
      tags:
        - Video Plans
      operationId: branchOrderedMediaSession
      summary: Create a new unapproved aspect or framing revision
      parameters:
        - in: path
          name: sessionId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderedMediaBranchRequestV1'
      responses:
        '201':
          description: New deterministic draft branch
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderedMediaSessionV1'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Base revision or idempotency conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /usage/receipts/{receiptId}:
    get:
      tags:
        - Usage
      operationId: getOrderedMediaUsageReceipt
      summary: Read immutable terminal ordered-media reconciliation
      parameters:
        - in: path
          name: receiptId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Captured or released terminal receipt
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderedMediaUsageReceiptV1'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Receipt exists but reconciliation is not terminal
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /render-runs/{runId}:
    get:
      tags:
        - Video Plans
      operationId: getRenderRun
      summary: Read one render run owned by the authenticated API client
      description: Poll this resource after approval when an integration does not use server-sent events.
      parameters:
        - in: path
          name: runId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Current render lifecycle and output state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderedMediaRenderRunV1'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /workflow-node-types:
    get:
      tags:
        - Workflow Node Types
      operationId: listWorkflowNodeTypes
      summary: Discover executable workflow node schemas, ports, versions, and limits
      description: |
        Returns the exact server-owned node registry used by executable workflow
        profile validation and compilation. Reading this catalog performs no
        provider calls and consumes no credits. Requires `catalog:read`.
      responses:
        '200':
          description: Versioned executable workflow node catalog
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowNodeTypeList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /workflow-profiles:
    get:
      tags:
        - Workflow Profiles
      operationId: listWorkflowProfiles
      summary: List granted platform profiles and client-authored profiles
      responses:
        '200':
          description: Published workflow profiles
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/WorkflowProfile'
                required:
                  - data
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags:
        - Workflow Profiles
      operationId: createWorkflowProfileDraft
      summary: Create a deterministic workflow profile draft
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkflowProfileDraftCreateRequest'
      responses:
        '201':
          description: Workflow profile draft created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowProfileDraft'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          description: Idempotency body conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          $ref: '#/components/responses/ValidationError'
  /workflow-profiles/{profileKey}:
    get:
      tags:
        - Workflow Profiles
      operationId: getWorkflowProfile
      summary: Read a workflow profile draft or published version
      parameters:
        - in: path
          name: profileKey
          required: true
          schema:
            type: string
        - in: query
          name: version
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Workflow profile detail or selected published version
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/WorkflowProfileDetail'
                  - $ref: '#/components/schemas/WorkflowProfile'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Workflow Profiles
      operationId: updateWorkflowProfileDraft
      summary: Update the latest workflow profile draft
      parameters:
        - in: path
          name: profileKey
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkflowProfileDraftUpdateRequest'
      responses:
        '200':
          description: Updated workflow profile draft
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowProfileDraft'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationError'
  /workflow-profiles/{profileKey}/preview:
    post:
      tags:
        - Workflow Profiles
      operationId: previewWorkflowProfile
      summary: Compile a provider-free workflow profile preview
      parameters:
        - in: path
          name: profileKey
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkflowProfilePreviewRequest'
      responses:
        '200':
          description: Workflow profile preview
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowProfilePreview'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Profile is not executable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /workflow-profiles/{profileKey}/publish:
    post:
      tags:
        - Workflow Profiles
      operationId: publishWorkflowProfile
      summary: Publish an immutable workflow profile version
      parameters:
        - in: path
          name: profileKey
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkflowProfilePublishRequest'
      responses:
        '201':
          description: Published workflow profile
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowProfile'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Published version already exists or idempotency conflicts
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /workflow-profile-bindings:
    get:
      tags:
        - Workflow Profile Bindings
      operationId: listWorkflowProfileBindings
      summary: List reusable bindings owned by the authenticated API client
      parameters:
        - in: query
          name: profile_key
          schema:
            type: string
        - in: query
          name: scope_level
          schema:
            type: string
            enum:
              - organization
              - team
              - user
        - in: query
          name: external_id
          schema:
            type: string
      responses:
        '200':
          description: Workflow profile bindings
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/WorkflowProfileBinding'
                required:
                  - data
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags:
        - Workflow Profile Bindings
      operationId: createWorkflowProfileBinding
      summary: Create a reusable binding to a published workflow profile
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkflowProfileBindingRequest'
      responses:
        '201':
          description: Workflow profile binding created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowProfileBinding'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          description: Idempotency body conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          $ref: '#/components/responses/ValidationError'
  /workflow-profile-bindings/{bindingId}:
    get:
      tags:
        - Workflow Profile Bindings
      operationId: getWorkflowProfileBinding
      summary: Read one binding owned by the authenticated API client
      parameters:
        - in: path
          name: bindingId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Workflow profile binding
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowProfileBinding'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Workflow Profile Bindings
      operationId: updateWorkflowProfileBinding
      summary: Update one binding owned by the authenticated API client
      parameters:
        - in: path
          name: bindingId
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkflowProfileBindingUpdateRequest'
      responses:
        '200':
          description: Updated workflow profile binding
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowProfileBinding'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationError'
  /capabilities:
    get:
      tags:
        - Capabilities
      operationId: getCapabilities
      summary: Discover supported creation surfaces and option sets
      security: []
      responses:
        '200':
          description: Capability catalog
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
  /api-keys:
    get:
      tags:
        - API Keys
      operationId: listApiKeys
      summary: List managed API keys
      description: |
        Lists database-backed API keys for the currently authenticated Shotmatic app session.

        This management surface is session-authenticated today. Managed API keys created
        here are intended to be used against the other `/v1` resources.
      responses:
        '200':
          description: API key registry
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyCollection'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags:
        - API Keys
      operationId: createApiKey
      summary: Create a managed API key
      description: |
        Creates a new database-backed API key and returns the plaintext token once.

        This operation requires an authenticated Shotmatic app session and does not
        accept an existing managed API key for self-service key creation.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateApiKeyRequest'
            examples:
              scopedKey:
                summary: Create a scoped key for video automation
                value:
                  name: Automation key
                  scopes:
                    - voices:read
                    - videos:read
                    - videos:write
                  expires_at: '2026-12-31T23:59:59Z'
                  metadata:
                    environment: production
      responses:
        '201':
          description: API key created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyWithToken'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'
  /api-keys/{keyId}:
    get:
      tags:
        - API Keys
      operationId: getApiKey
      summary: Get one managed API key
      description: Returns metadata for a single managed API key owned by the current app session user.
      parameters:
        - $ref: '#/components/parameters/KeyId'
      responses:
        '200':
          description: API key metadata
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKey'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - API Keys
      operationId: updateApiKey
      summary: Update one managed API key
      description: Updates the name, scopes, expiry, or metadata for a key owned by the current app session user.
      parameters:
        - $ref: '#/components/parameters/KeyId'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateApiKeyRequest'
      responses:
        '200':
          description: Updated API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKey'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationError'
    delete:
      tags:
        - API Keys
      operationId: revokeApiKey
      summary: Revoke one managed API key
      description: Revokes a key owned by the current app session user. Revoked keys can no longer be used for bearer-authenticated `/v1` access.
      parameters:
        - $ref: '#/components/parameters/KeyId'
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          description: Revoked API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKey'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /soundtracks:
    get:
      tags:
        - Soundtracks
      operationId: listSoundtracks
      summary: List stable Shotmatic soundtrack choices
      description: Reads the packaged Shotmatic catalog only. It does not call a music or generation provider.
      responses:
        '200':
          description: Soundtrack catalog
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Soundtrack'
                required:
                  - data
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /voice-catalog:
    get:
      tags:
        - Voice Catalog
      operationId: listVoiceCatalog
      summary: List curated provider-neutral Voice import choices
      description: Reads Shotmatic catalog metadata and retained private-R2 previews. It never calls a live provider catalog or synthesizes audio.
      parameters:
        - in: query
          name: locale
          schema:
            type: string
            maxLength: 35
      responses:
        '200':
          description: Ready Voice Catalog Entries
          content:
            application/json:
              schema:
                type: object
                additionalProperties: false
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/VoiceCatalogEntry'
                required:
                  - data
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /voice-catalog/{catalogEntryId}:
    get:
      tags:
        - Voice Catalog
      operationId: getVoiceCatalogEntry
      summary: Read one curated provider-neutral Voice choice
      parameters:
        - in: path
          name: catalogEntryId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Voice Catalog Entry
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VoiceCatalogEntry'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /voice-catalog/{catalogEntryId}/preview:
    get:
      tags:
        - Voice Catalog
      operationId: previewVoiceCatalogEntry
      summary: Open one retained Voice Catalog preview
      description: Returns a short-lived redirect to immutable private-R2 audio. It never synthesizes audio.
      parameters:
        - in: path
          name: catalogEntryId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '307':
          description: Retained private-R2 preview redirect
          headers:
            Location:
              schema:
                type: string
                format: uri
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /voices:
    get:
      tags:
        - Voices
      operationId: listVoices
      summary: List Account Voices
      description: Returns only provider-neutral Voice Identities owned by the caller account. Provider Voice IDs, models, and settings are never exposed.
      parameters:
        - in: query
          name: locale
          schema:
            type: string
            maxLength: 35
        - in: query
          name: state
          schema:
            type: string
            enum:
              - draft
              - provisioning
              - ready
              - disabled
              - failed
      responses:
        '200':
          description: Account Voice collection
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Voice'
                required:
                  - data
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags:
        - Voices
      operationId: importAccountVoice
      summary: Import one curated Voice into the caller account
      description: Adopts the retained preview into account-owned private R2 media and atomically creates or reuses one provider-neutral Voice plus a service-only ready binding. It performs no synthesis or live provider catalog call.
      parameters:
        - $ref: '#/components/parameters/RequiredIdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AccountVoiceImportRequest'
      responses:
        '201':
          description: Imported or idempotently reused Account Voice
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Voice'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
  /voices/{voiceId}:
    get:
      tags:
        - Voices
      operationId: getVoice
      summary: Get one Account Voice
      parameters:
        - $ref: '#/components/parameters/VoiceId'
      responses:
        '200':
          description: Voice details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Voice'
        '404':
          $ref: '#/components/responses/NotFound'
  /voices/{voiceId}/preview:
    get:
      tags:
        - Voices
      operationId: previewVoice
      summary: Open the retained Account Voice preview
      description: Returns a short-lived redirect to immutable account-owned private-R2 audio. It never accepts text or performs synthesis.
      parameters:
        - $ref: '#/components/parameters/VoiceId'
      responses:
        '307':
          description: Retained private-R2 preview redirect
          headers:
            Location:
              schema:
                type: string
                format: uri
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /media:
    get:
      tags:
        - Media
      operationId: listMedia
      summary: List uploaded media
      parameters:
        - in: query
          name: type
          schema:
            type: string
            enum:
              - video
              - image
              - audio
        - in: query
          name: sort
          schema:
            type: string
            enum:
              - recent
              - name
              - size
            default: recent
      responses:
        '200':
          description: Media library
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/MediaAsset'
                  total:
                    type: integer
                    minimum: 0
                required:
                  - data
                  - total
    post:
      tags:
        - Media
      operationId: uploadMedia
      summary: Upload source media
      description: |
        Uploads a source asset that can later be used in a clip task or video plan.
        This endpoint is intended for public API consumers and replaces the current
        UI-specific upload flow.
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                kind:
                  type: string
                  enum:
                    - video
                    - image
                    - audio
                title:
                  type: string
                metadata:
                  type: string
                  description: JSON-encoded metadata blob.
              required:
                - file
                - kind
      responses:
        '201':
          description: Uploaded media asset
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaAsset'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          description: File too large
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /media/{mediaId}:
    get:
      tags:
        - Media
      operationId: getMedia
      summary: Get one media asset
      parameters:
        - $ref: '#/components/parameters/MediaId'
      responses:
        '200':
          description: Media details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaAsset'
        '404':
          $ref: '#/components/responses/NotFound'
  /media/uploads:
    post:
      tags:
        - Media
      operationId: createDirectMediaUpload
      summary: Initiate a checksum-bound R2 multipart upload
      description: |
        Freezes 16 MiB ordered parts and their SHA-256 checksums before creating
        provider state. Use this path for video that should not cross a Vercel
        request body. Pending objects are not visible as media assets.
      parameters:
        - $ref: '#/components/parameters/RequiredIdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateDirectMediaUploadRequest'
      responses:
        '201':
          description: Durable upload state and short-lived part URLs
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DirectMediaUploadResponse'
        '409':
          description: Idempotency key conflicts with another frozen manifest
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /media/uploads/{uploadId}:
    parameters:
      - $ref: '#/components/parameters/UploadId'
    get:
      tags:
        - Media
      operationId: getDirectMediaUpload
      summary: Inspect one direct upload owned by the authenticated API client
      responses:
        '200':
          description: Direct upload state
          content:
            application/json:
              schema:
                type: object
                properties:
                  upload:
                    $ref: '#/components/schemas/DirectMediaUpload'
                  media:
                    $ref: '#/components/schemas/MediaAsset'
                required:
                  - upload
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
        - Media
      operationId: abortDirectMediaUpload
      summary: Abort provider state and delete any incomplete object
      responses:
        '200':
          description: Aborted or replayed upload state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DirectMediaUploadResponse'
  /media/uploads/{uploadId}/parts:
    post:
      tags:
        - Media
      operationId: refreshDirectMediaUploadParts
      summary: Refresh selected short-lived part URLs
      parameters:
        - $ref: '#/components/parameters/UploadId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RefreshDirectMediaUploadPartsRequest'
      responses:
        '200':
          description: Refreshed checksum-bound part URLs
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DirectMediaUploadResponse'
        '409':
          description: Upload is expired or no longer accepting parts
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /media/uploads/{uploadId}/complete:
    post:
      tags:
        - Media
      operationId: completeDirectMediaUpload
      summary: Complete and promote a verified multipart upload
      parameters:
        - $ref: '#/components/parameters/UploadId'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompleteDirectMediaUploadRequest'
      responses:
        '201':
          description: Completed upload and usable media asset
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompleteDirectMediaUploadResponse'
        '409':
          description: Frozen manifest conflict or active completion lease
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /video-plans:
    get:
      tags:
        - Video Plans
      operationId: listVideoPlans
      summary: List video plans
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/PageSize'
        - in: query
          name: status
          schema:
            $ref: '#/components/schemas/VideoPlanStatus'
      responses:
        '200':
          description: Video plans
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/VideoPlan'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
                required:
                  - data
                  - pagination
    post:
      tags:
        - Video Plans
      operationId: createVideoPlan
      summary: Create an interactive video plan
      description: |
        Accepts a new interactive plan request and starts draft generation in the background.

        Use this mode when the client wants a human-in-the-loop workflow:
        script generation, scene planning, voice selection, and render settings
        can all be adjusted before approval.

        The initial response returns a persisted `drafting` plan immediately.
        Clients should poll `GET /video-plans/{planId}` until the plan reaches
        `ready_for_review` or `failed`.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateVideoPlanRequest'
            examples:
              articlePlan:
                summary: Draft a plan from an article
                value:
                  source:
                    kind: article_url
                    article_url: https://example.com/viral-story
                  goal:
                    title: How this product launch went viral
                    language: en-US
                    duration_seconds: 45
                    visual_style: ugc
                    target_platform: tiktok
                  defaults:
                    voice:
                      id: bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb
                    render:
                      aspect_ratio: '9:16'
                      media_type: images_only
                      image_quality: standard
                      include_soundtrack: true
      responses:
        '202':
          description: Draft generation accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VideoPlan'
        '422':
          $ref: '#/components/responses/ValidationError'
  /video-plans/{planId}:
    get:
      tags:
        - Video Plans
      operationId: getVideoPlan
      summary: Get one video plan
      parameters:
        - $ref: '#/components/parameters/PlanId'
      responses:
        '200':
          description: Video plan
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VideoPlan'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags:
        - Video Plans
      operationId: updateVideoPlan
      summary: Update an interactive video plan
      description: |
        Applies user edits before approval. This is the primary API for the
        interactive creation flow.
      parameters:
        - $ref: '#/components/parameters/PlanId'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateVideoPlanRequest'
      responses:
        '200':
          description: Updated plan
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VideoPlan'
        '409':
          description: Plan is no longer editable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /video-plans/{planId}/approve:
    post:
      tags:
        - Video Plans
      operationId: approveVideoPlan
      summary: Approve a video plan and start rendering
      parameters:
        - $ref: '#/components/parameters/PlanId'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApproveVideoPlanRequest'
      responses:
        '202':
          description: Rendering accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Video'
        '409':
          description: Plan already approved or no longer valid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /videos:
    get:
      tags:
        - Videos
      operationId: listVideos
      summary: List videos
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/PageSize'
        - in: query
          name: status
          schema:
            $ref: '#/components/schemas/VideoStatus'
      responses:
        '200':
          description: Videos
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Video'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
                required:
                  - data
                  - pagination
    post:
      tags:
        - Videos
      operationId: createVideo
      summary: Create a video in fully automated mode
      description: |
        One-shot API for clients that do not need a review step.
        Shotmatic will plan, generate, and render immediately.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateVideoRequest'
            examples:
              oneShotScript:
                summary: Generate a video directly from a script
                value:
                  source:
                    kind: script
                    script: |
                      Three hooks that stop the scroll.
                      First, start with a surprising claim.
                      Second, show the proof immediately.
                      Third, end with one simple CTA.
                  title: Three hooks that stop the scroll
                  language: en-US
                  voice:
                    id: bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb
                  render:
                    aspect_ratio: '9:16'
                    duration_seconds: 30
                    visual_style: ugc
                    media_type: images_only
                    image_quality: standard
                    include_soundtrack: true
                  webhook_url: https://client.example.com/hooks/shotmatic
                  webhook_secret: whsec_client_shared_secret
      responses:
        '202':
          description: Video queued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Video'
  /videos/{videoId}:
    get:
      tags:
        - Videos
      operationId: getVideo
      summary: Get one video job
      parameters:
        - $ref: '#/components/parameters/VideoId'
      responses:
        '200':
          description: Video details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Video'
        '404':
          $ref: '#/components/responses/NotFound'
  /videos/{videoId}/cancel:
    post:
      tags:
        - Videos
      operationId: cancelVideo
      summary: Cancel a queued or running video
      parameters:
        - $ref: '#/components/parameters/VideoId'
      responses:
        '202':
          description: Cancellation requested
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  status:
                    $ref: '#/components/schemas/VideoStatus'
                required:
                  - id
                  - status
  /clip-tasks:
    post:
      tags:
        - Clip Tasks
      operationId: createClipTask
      summary: Create a clip extraction task
      description: |
        Starts transcription and clip sectioning for either an uploaded file
        or a source URL.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateClipTaskRequest'
          multipart/form-data:
            schema:
              allOf:
                - $ref: '#/components/schemas/CreateClipTaskUploadRequest'
      responses:
        '202':
          description: Clip task queued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClipTask'
  /clip-tasks/{clipTaskId}:
    get:
      tags:
        - Clip Tasks
      operationId: getClipTask
      summary: Get one clip task
      parameters:
        - $ref: '#/components/parameters/ClipTaskId'
      responses:
        '200':
          description: Clip task
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClipTask'
        '404':
          $ref: '#/components/responses/NotFound'
  /clip-tasks/{clipTaskId}/render:
    post:
      tags:
        - Clip Tasks
      operationId: renderClipSelection
      summary: Render one or more selected clips into videos
      parameters:
        - $ref: '#/components/parameters/ClipTaskId'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RenderClipSelectionRequest'
      responses:
        '202':
          description: Render accepted
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Video'
                  video_id:
                    type: string
                    description: Present when the render request creates a single video.
                  video:
                    $ref: '#/components/schemas/Video'
                required:
                  - data
  /workflows:
    get:
      tags:
        - Workflows
      operationId: listWorkflows
      summary: List workflows available to the caller
      responses:
        '200':
          description: Workflow catalog
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Workflow'
                required:
                  - data
  /workflows/{workflowId}/executions:
    get:
      tags:
        - Workflows
      operationId: listWorkflowExecutions
      summary: List executions for one workflow
      parameters:
        - $ref: '#/components/parameters/WorkflowId'
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/PageSize'
      responses:
        '200':
          description: Workflow executions
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/WorkflowExecution'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
                required:
                  - data
                  - pagination
    post:
      tags:
        - Workflows
      operationId: executeWorkflow
      summary: Start a workflow execution
      parameters:
        - $ref: '#/components/parameters/WorkflowId'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWorkflowExecutionRequest'
      responses:
        '202':
          description: Workflow execution queued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowExecution'
  /workflow-executions/{executionId}:
    get:
      tags:
        - Workflows
      operationId: getWorkflowExecution
      summary: Get one workflow execution
      parameters:
        - $ref: '#/components/parameters/ExecutionId'
      responses:
        '200':
          description: Workflow execution
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowExecution'
        '404':
          $ref: '#/components/responses/NotFound'
  /webhooks/endpoints:
    get:
      tags:
        - Webhooks
      operationId: listWebhookEndpoints
      summary: List registered signed-webhook endpoints
      responses:
        '200':
          description: Webhook endpoints
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookEndpoint'
                required:
                  - data
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags:
        - Webhooks
      operationId: createWebhookEndpoint
      summary: Verify and register a signed-webhook endpoint
      description: Sends an endpoint.verification challenge, then returns the signing secret exactly once.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWebhookRequest'
      responses:
        '201':
          description: Webhook endpoint created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookEndpointCreated'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: Endpoint or idempotency conflict
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          $ref: '#/components/responses/ValidationError'
  /webhooks/endpoints/{endpointId}:
    patch:
      tags:
        - Webhooks
      operationId: updateWebhookEndpoint
      summary: Disable, reactivate, or rotate a registered endpoint secret
      parameters:
        - in: path
          name: endpointId
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEndpointMutationRequest'
      responses:
        '200':
          description: Updated endpoint, with a new secret returned once after rotation
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationError'
  /webhooks:
    get:
      deprecated: true
      tags:
        - Webhooks
      operationId: listWebhookEndpointsLegacy
      summary: Legacy alias for GET /webhooks/endpoints
      responses:
        '200':
          description: Webhook endpoints
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/WebhookEndpoint'
                required:
                  - data
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      deprecated: true
      tags:
        - Webhooks
      operationId: createWebhookEndpointLegacy
      summary: Legacy alias for POST /webhooks/endpoints
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWebhookRequest'
      responses:
        '201':
          description: Webhook endpoint created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookEndpointCreated'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'
  /webhooks/deliveries:
    get:
      tags:
        - Webhooks
      operationId: listWebhookDeliveries
      summary: List webhook deliveries
      description: Lists outbound webhook delivery attempts, retry state, and the last failure details for the current account.
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/PageSize'
        - in: query
          name: status
          schema:
            $ref: '#/components/schemas/WebhookDeliveryStatus'
        - in: query
          name: event_type
          schema:
            $ref: '#/components/schemas/WebhookEventType'
        - in: query
          name: video_id
          schema:
            type: string
      responses:
        '200':
          description: Webhook delivery attempts
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookDeliveryCollection'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationError'
  /webhooks/deliveries/{deliveryId}:
    get:
      tags:
        - Webhooks
      operationId: getWebhookDelivery
      summary: Get one webhook delivery
      description: Returns a single outbound webhook delivery with debug fields such as the response body and payload.
      parameters:
        - $ref: '#/components/parameters/DeliveryId'
      responses:
        '200':
          description: Webhook delivery
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookDeliveryDetail'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /usage:
    get:
      tags:
        - Usage
      operationId: getUsage
      summary: Get current account usage
      responses:
        '200':
          description: Current usage snapshot
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UsageSummary'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: APIKey
  parameters:
    Page:
      in: query
      name: page
      schema:
        type: integer
        minimum: 1
        default: 1
    PageSize:
      in: query
      name: page_size
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
    IdempotencyKey:
      in: header
      name: Idempotency-Key
      required: false
      schema:
        type: string
        maxLength: 255
      description: Recommended for all mutating requests.
    VoiceId:
      in: path
      name: voiceId
      required: true
      schema:
        type: string
        format: uuid
    MediaId:
      in: path
      name: mediaId
      required: true
      schema:
        type: string
    UploadId:
      in: path
      name: uploadId
      required: true
      schema:
        type: string
        format: uuid
    PlanId:
      in: path
      name: planId
      required: true
      schema:
        type: string
    KeyId:
      in: path
      name: keyId
      required: true
      schema:
        type: string
    VideoId:
      in: path
      name: videoId
      required: true
      schema:
        type: string
    ClipTaskId:
      in: path
      name: clipTaskId
      required: true
      schema:
        type: string
    WorkflowId:
      in: path
      name: workflowId
      required: true
      schema:
        type: string
    ExecutionId:
      in: path
      name: executionId
      required: true
      schema:
        type: string
    DeliveryId:
      in: path
      name: deliveryId
      required: true
      schema:
        type: string
    AvatarPickerStrategy:
      in: query
      name: strategy
      required: false
      description: Final Shotmatic composition strategy used to evaluate Profile compatibility. This does not call an avatar provider.
      schema:
        type: string
        enum:
          - bookends
          - floating_host
          - split_host
          - presenter_first
          - scene_cameos
          - full_presenter
        default: bookends
    AvatarPickerAspectRatio:
      in: query
      name: aspect_ratio
      required: false
      description: Deterministic final-video aspect ratio used to evaluate the active provider binding.
      schema:
        type: string
        enum:
          - '9:16'
          - '16:9'
          - '1:1'
          - '4:5'
          - '5:4'
        default: '9:16'
    AvatarPickerDurationSeconds:
      in: query
      name: duration_seconds
      required: false
      description: Expected speaking duration used only for compatibility and cost-state evaluation.
      schema:
        type: number
        exclusiveMinimum: 0
        maximum: 3600
        default: 30
    AvatarPickerLocale:
      in: query
      name: locale
      required: false
      description: Requested speech locale used to evaluate adapter capability constraints.
      schema:
        type: string
        minLength: 2
        maxLength: 35
        default: en
    AvatarPickerOutputBackground:
      in: query
      name: output_background
      required: false
      description: Raw Avatar Performance background needed by the selected Remotion composition strategy.
      schema:
        type: string
        enum:
          - transparent
          - solid
          - source
        default: source
    AvatarPickerAudioSha256:
      in: query
      name: audio_sha256
      required: false
      description: Optional immutable audio SHA-256. When supplied, availability reports a cache hit only for the exact account-owned Performance request.
      schema:
        type: string
        pattern: ^[a-f0-9]{64}$
    RequiredIdempotencyKey:
      in: header
      name: Idempotency-Key
      required: true
      schema:
        type: string
        minLength: 1
        maxLength: 255
      description: Required before Shotmatic creates durable provider state.
  responses:
    Unauthorized:
      description: Missing or invalid credentials
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ValidationError:
      description: Validation error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: Authenticated but not allowed to access the resource or scope
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Conflict:
      description: Resource state or frozen quote conflict
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ServiceUnavailable:
      description: Required provider adapter or service is unavailable
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
        code:
          type: string
        details:
          oneOf:
            - type: object
              additionalProperties: true
            - type: array
              items:
                type: object
                additionalProperties: true
            - type: string
      required:
        - error
    Pagination:
      type: object
      properties:
        page:
          type: integer
        page_size:
          type: integer
        total:
          type: integer
        total_pages:
          type: integer
      required:
        - page
        - page_size
        - total
        - total_pages
    Voice:
      type: object
      additionalProperties: false
      description: Account-owned provider-neutral Voice Identity. Provider resources, models, and settings are service-only.
      properties:
        object:
          const: voice
        id:
          type: string
          format: uuid
          description: Shotmatic Account Voice UUID.
        name:
          type: string
        language:
          type: string
        locale:
          type: string
        gender:
          type:
            - string
            - 'null'
        description:
          type:
            - string
            - 'null'
        preview_url:
          type:
            - string
            - 'null'
          format: uri
          description: Retained private-R2 preview endpoint. Reading it never synthesizes audio.
        source_kind:
          type: string
          enum:
            - curated_catalog
        rights_scope:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
        state:
          type: string
          enum:
            - draft
            - provisioning
            - ready
            - disabled
            - failed
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - object
        - id
        - name
        - description
        - language
        - locale
        - preview_url
        - source_kind
        - rights_scope
        - state
        - created_at
        - updated_at
    VoiceCatalogEntry:
      type: object
      additionalProperties: false
      description: Provider-neutral curated Voice choice that can be imported into one account without a live provider catalog or synthesis call.
      properties:
        object:
          const: voice_catalog_entry
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type:
            - string
            - 'null'
        language:
          type: string
        locale:
          type: string
        rights_scope:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
        state:
          type: string
          enum:
            - ready
        preview_url:
          type:
            - string
            - 'null'
          format: uri
          description: Retained private-R2 preview endpoint. Reading it never synthesizes audio.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - object
        - id
        - name
        - description
        - language
        - locale
        - rights_scope
        - state
        - preview_url
        - created_at
        - updated_at
    AccountVoiceImportRequest:
      type: object
      additionalProperties: false
      properties:
        catalog_entry_id:
          type: string
          format: uuid
        name:
          type: string
          minLength: 1
          maxLength: 120
      required:
        - catalog_entry_id
    VoiceRef:
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
          description: Account Voice UUID.
      required:
        - id
    MediaAsset:
      type: object
      properties:
        id:
          type: string
        kind:
          type: string
          enum:
            - video
            - image
            - audio
        title:
          type: string
        file_name:
          type: string
        mime_type:
          type: string
        file_size:
          type: integer
          minimum: 0
        url:
          type: string
          format: uri
        duration_seconds:
          type:
            - number
            - 'null'
        created_at:
          type: string
          format: date-time
        metadata:
          type: object
          additionalProperties: true
      required:
        - id
        - kind
        - file_name
        - mime_type
        - file_size
        - url
        - created_at
        - metadata
    DirectMediaUploadPartRequest:
      type: object
      additionalProperties: false
      properties:
        part_number:
          type: integer
          minimum: 1
          maximum: 10000
        size_bytes:
          type: integer
          minimum: 1
          maximum: 16777216
        checksum_sha256:
          type: string
          description: Base64-encoded SHA-256 identity for this exact part, frozen for replay and audit.
        content_md5:
          type: string
          description: Base64-encoded MD5 transport checksum for this exact part; R2 validates Content-MD5 on UploadPart.
      required:
        - part_number
        - size_bytes
        - checksum_sha256
        - content_md5
    DirectMediaUploadPartUrl:
      allOf:
        - $ref: '#/components/schemas/DirectMediaUploadPartRequest'
        - type: object
          properties:
            upload_url:
              type: string
              format: uri
            required_headers:
              type: object
              additionalProperties:
                type: string
          required:
            - upload_url
            - required_headers
    DirectMediaUpload:
      type: object
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          enum:
            - initiated
            - completing
            - completed
            - aborted
            - failed
        file_name:
          type: string
        mime_type:
          type: string
        kind:
          type: string
          enum:
            - video
            - image
            - audio
        file_size:
          type: integer
          minimum: 1
          maximum: 524288000
        content_sha256:
          type: string
          pattern: ^sha256:[0-9a-f]{64}$
          description: Client full-object identity frozen with the manifest; R2 separately validates each part's Content-MD5 transport checksum.
        manifest_sha256:
          type: string
          pattern: ^sha256:[0-9a-f]{64}$
        part_size_bytes:
          type: integer
          const: 16777216
        part_count:
          type: integer
          minimum: 1
        expires_at:
          type: string
          format: date-time
        media_id:
          type:
            - string
            - 'null'
          format: uuid
        error:
          type:
            - string
            - 'null'
      required:
        - id
        - status
        - file_name
        - mime_type
        - kind
        - file_size
        - content_sha256
        - manifest_sha256
        - part_size_bytes
        - part_count
        - expires_at
        - media_id
        - error
    CreateDirectMediaUploadRequest:
      type: object
      additionalProperties: false
      properties:
        file_name:
          type: string
          minLength: 1
          maxLength: 255
        mime_type:
          type: string
        kind:
          type: string
          enum:
            - video
            - image
            - audio
        file_size:
          type: integer
          minimum: 1
          maximum: 524288000
        content_sha256:
          type: string
          pattern: ^sha256:[0-9a-f]{64}$
        parts:
          type: array
          minItems: 1
          maxItems: 10000
          items:
            $ref: '#/components/schemas/DirectMediaUploadPartRequest'
        title:
          type: string
        metadata:
          type: object
          additionalProperties: true
      required:
        - file_name
        - mime_type
        - kind
        - file_size
        - content_sha256
        - parts
    DirectMediaUploadResponse:
      type: object
      properties:
        upload:
          $ref: '#/components/schemas/DirectMediaUpload'
        parts:
          type: array
          items:
            $ref: '#/components/schemas/DirectMediaUploadPartUrl'
        replayed:
          type: boolean
      required:
        - upload
        - replayed
    DirectMediaUploadPartCompletion:
      type: object
      additionalProperties: false
      properties:
        part_number:
          type: integer
          minimum: 1
          maximum: 10000
        etag:
          type: string
          minLength: 1
      required:
        - part_number
        - etag
    CompleteDirectMediaUploadRequest:
      type: object
      additionalProperties: false
      properties:
        parts:
          type: array
          minItems: 1
          maxItems: 10000
          items:
            $ref: '#/components/schemas/DirectMediaUploadPartCompletion'
      required:
        - parts
    CompleteDirectMediaUploadResponse:
      type: object
      properties:
        upload:
          $ref: '#/components/schemas/DirectMediaUpload'
        media:
          $ref: '#/components/schemas/MediaAsset'
        replayed:
          type: boolean
      required:
        - upload
        - media
        - replayed
    RefreshDirectMediaUploadPartsRequest:
      type: object
      additionalProperties: false
      properties:
        part_numbers:
          type: array
          minItems: 1
          maxItems: 100
          uniqueItems: true
          items:
            type: integer
            minimum: 1
            maximum: 10000
      required:
        - part_numbers
    ApiKeyScope:
      type: string
      enum:
        - '*'
        - voice_catalog:read
        - voices:read
        - voices:write
        - catalog:read
        - media:read
        - media:write
        - videos:read
        - videos:write
        - sessions:read
        - sessions:write
        - projects:read
        - projects:write
        - brand_kits:read
        - brand_kits:write
        - presenter_profiles:read
        - presenter_profiles:write
        - avatar_profiles:read
        - avatar_profiles:write
        - avatar_source_packs:read
        - avatar_source_packs:write
        - workflow_profiles:read
        - workflow_profiles:write
        - workflow_bindings:read
        - workflow_bindings:write
        - clip_tasks:read
        - clip_tasks:write
        - workflows:read
        - workflow_executions:read
        - workflow_executions:write
        - webhooks:read
        - webhooks:write
        - usage:read
    ApiKey:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        key_prefix:
          type: string
        token_preview:
          type: string
        scopes:
          type: array
          items:
            $ref: '#/components/schemas/ApiKeyScope'
        expires_at:
          type:
            - string
            - 'null'
          format: date-time
        last_used_at:
          type:
            - string
            - 'null'
          format: date-time
        revoked_at:
          type:
            - string
            - 'null'
          format: date-time
        metadata:
          type: object
          additionalProperties: true
          description: Non-secret metadata. Operator-managed API client authorization fields may appear here.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - name
        - key_prefix
        - token_preview
        - scopes
        - metadata
        - created_at
        - updated_at
    ApiKeyWithToken:
      allOf:
        - $ref: '#/components/schemas/ApiKey'
        - type: object
          properties:
            token:
              type: string
              description: Returned only once at creation time.
          required:
            - token
    ApiKeyCollection:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/ApiKey'
      required:
        - data
    CreateApiKeyRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 120
        scopes:
          type: array
          maxItems: 32
          items:
            $ref: '#/components/schemas/ApiKeyScope'
        expires_at:
          type: string
          format: date-time
        metadata:
          type: object
          additionalProperties: true
          description: Free-form non-privileged metadata. client_id, client_type, workflow_profile_grants, and ordered_media_access are rejected.
      required:
        - name
      additionalProperties: false
    UpdateApiKeyRequest:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 120
        scopes:
          type: array
          maxItems: 32
          items:
            $ref: '#/components/schemas/ApiKeyScope'
        expires_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
        metadata:
          type: object
          additionalProperties: true
          description: Free-form non-privileged metadata. client_id, client_type, workflow_profile_grants, and ordered_media_access are rejected.
      additionalProperties: false
    SourceInput:
      oneOf:
        - $ref: '#/components/schemas/PromptSourceInput'
        - $ref: '#/components/schemas/ScriptSourceInput'
        - $ref: '#/components/schemas/ArticleUrlSourceInput'
        - $ref: '#/components/schemas/WorkflowProfileSourceInput'
        - $ref: '#/components/schemas/WorkflowProfileBindingSourceInput'
      discriminator:
        propertyName: kind
        mapping:
          prompt: '#/components/schemas/PromptSourceInput'
          script: '#/components/schemas/ScriptSourceInput'
          article_url: '#/components/schemas/ArticleUrlSourceInput'
          workflow_profile: '#/components/schemas/WorkflowProfileSourceInput'
          workflow_profile_binding: '#/components/schemas/WorkflowProfileBindingSourceInput'
    PromptSourceInput:
      type: object
      properties:
        kind:
          const: prompt
        prompt:
          type: string
      required:
        - kind
        - prompt
    ScriptSourceInput:
      type: object
      properties:
        kind:
          const: script
        script:
          type: string
      required:
        - kind
        - script
    ArticleUrlSourceInput:
      type: object
      properties:
        kind:
          const: article_url
        article_url:
          type: string
          format: uri
      required:
        - kind
        - article_url
    MediaSourceInput:
      type: object
      properties:
        kind:
          const: media
        media_id:
          type: string
      required:
        - kind
        - media_id
    ClipSourceInput:
      type: object
      properties:
        kind:
          const: clip
        clip_task_id:
          type: string
        section_index:
          type: integer
          minimum: 0
      required:
        - kind
        - clip_task_id
        - section_index
    RenderOptions:
      type: object
      properties:
        aspect_ratio:
          type: string
          enum:
            - '9:16'
            - '4:5'
            - '1:1'
            - '16:9'
          default: '9:16'
        duration_seconds:
          type: integer
          minimum: 15
          maximum: 180
        visual_style:
          type: string
          description: Public-facing visual style token.
        media_type:
          type: string
          enum:
            - images_only
            - first_only
            - strong_start
            - odds
            - full
          default: images_only
        image_quality:
          type: string
          enum:
            - standard
            - ultra
          default: standard
        include_soundtrack:
          type: boolean
          default: true
        use_voice_over:
          type: boolean
          default: true
        camera_motion:
          $ref: '#/components/schemas/CameraMotion'
      additionalProperties: false
    Soundtrack:
      type: object
      description: Stable Shotmatic soundtrack catalog entry. Use its id and exact catalog URL in session creative controls.
      properties:
        id:
          type: string
        name:
          type: string
        url:
          type: string
          format: uri
        tags:
          type: array
          items:
            type: string
      required:
        - id
        - name
        - url
        - tags
      additionalProperties: false
    GoalSettings:
      type: object
      properties:
        title:
          type: string
        language:
          type: string
          default: en-US
        duration_seconds:
          type: integer
          minimum: 15
          maximum: 180
        visual_style:
          type: string
        target_platform:
          type: string
          enum:
            - tiktok
            - instagram_reels
            - youtube_shorts
            - linkedin
            - generic
          default: generic
      additionalProperties: false
    CreateVideoPlanRequest:
      type: object
      properties:
        source:
          $ref: '#/components/schemas/SourceInput'
        goal:
          $ref: '#/components/schemas/GoalSettings'
        project_id:
          type: string
          format: uuid
        brand_kit_id:
          type: string
          format: uuid
        mode:
          type: string
          enum:
            - quick
            - guided
            - asset_first
            - clip
            - workflow
        input_assets:
          type: array
          items:
            $ref: '#/components/schemas/SessionInputAsset'
        asset_bindings:
          type: array
          maxItems: 180
          items:
            $ref: '#/components/schemas/SessionAssetBinding'
        defaults:
          type: object
          properties:
            voice:
              $ref: '#/components/schemas/VoiceRef'
            render:
              $ref: '#/components/schemas/RenderOptions'
            caption_style:
              $ref: '#/components/schemas/SessionCaptionStyle'
          additionalProperties: false
        creative:
          $ref: '#/components/schemas/SessionCreativeControls'
        metadata:
          type: object
          additionalProperties: true
      required:
        - source
        - goal
    UpdateVideoPlanRequest:
      type: object
      properties:
        project_id:
          type: string
          format: uuid
        brand_kit_id:
          type: string
          format: uuid
        mode:
          type: string
          enum:
            - quick
            - guided
            - asset_first
            - clip
            - workflow
        title:
          type: string
        language:
          type: string
        script:
          type: string
        voice:
          $ref: '#/components/schemas/VoiceRef'
        caption_style:
          $ref: '#/components/schemas/SessionCaptionStyle'
        input_assets:
          type: array
          items:
            $ref: '#/components/schemas/SessionInputAsset'
        asset_bindings:
          type: array
          maxItems: 180
          items:
            $ref: '#/components/schemas/SessionAssetBinding'
        render:
          $ref: '#/components/schemas/RenderOptions'
        scenes:
          type: array
          items:
            $ref: '#/components/schemas/ScenePlan'
        creative:
          $ref: '#/components/schemas/SessionCreativeControls'
        metadata:
          type: object
          additionalProperties: true
      additionalProperties: false
    ApproveVideoPlanRequest:
      type: object
      properties:
        expected_session_version:
          type: integer
          minimum: 1
          description: Version reviewed by the caller. Approval fails if the session changed.
        max_credits:
          type: integer
          minimum: 1
          description: Caller-selected hard cap. Approval fails before queueing if the current estimate is higher.
        external_reference:
          type: string
          minLength: 1
          maxLength: 240
          description: Caller correlation value copied into request and usage metadata.
        voice:
          $ref: '#/components/schemas/VoiceRef'
        caption_style:
          $ref: '#/components/schemas/SessionCaptionStyle'
        render:
          $ref: '#/components/schemas/RenderOptions'
        webhook_url:
          type: string
          format: uri
        webhook_secret:
          type: string
          description: Optional HMAC secret used to sign outbound Shotmatic webhook deliveries. Signature format is `v1=<hex_hmac_sha256>` over `${timestamp}.${raw_json_body}`.
        avatar_performance_batch:
          $ref: '#/components/schemas/AvatarSessionBatchAuthorizationRequest'
      additionalProperties: false
    VideoPlanStatus:
      type: string
      enum:
        - drafting
        - draft
        - ready_for_review
        - approved
        - queued
        - rendering
        - completed
        - failed
        - expired
    ScenePlan:
      type: object
      properties:
        index:
          type: integer
          minimum: 0
        narration:
          type: string
        visual_prompt:
          type: string
        duration_seconds:
          type: number
        start_time:
          type:
            - number
            - 'null'
        end_time:
          type:
            - number
            - 'null'
        camera_motion:
          $ref: '#/components/schemas/CameraMotion'
      required:
        - index
        - narration
        - visual_prompt
        - duration_seconds
    TimestampWord:
      type: object
      properties:
        word:
          type: string
        start:
          type: number
        end:
          type: number
      required:
        - word
        - start
        - end
    Transcript:
      type: object
      properties:
        text:
          type: string
        words:
          type: array
          items:
            $ref: '#/components/schemas/TimestampWord'
      required:
        - text
        - words
    CostEstimate:
      type: object
      properties:
        credits:
          type: integer
          minimum: 0
        currency:
          type: string
          default: credits
        explanation:
          type: string
      required:
        - credits
        - currency
    VideoPlan:
      type: object
      properties:
        id:
          type: string
        status:
          $ref: '#/components/schemas/VideoPlanStatus'
        source:
          $ref: '#/components/schemas/SourceInput'
        title:
          type: string
        language:
          type: string
        script:
          type: string
        voice:
          $ref: '#/components/schemas/VoiceRef'
        input_assets:
          type: array
          items:
            $ref: '#/components/schemas/SessionInputAsset'
        asset_bindings:
          type: array
          items:
            $ref: '#/components/schemas/SessionAssetBinding'
        creative:
          $ref: '#/components/schemas/SessionCreativeControls'
        render:
          $ref: '#/components/schemas/RenderOptions'
        scenes:
          type: array
          items:
            $ref: '#/components/schemas/ScenePlan'
        transcript:
          anyOf:
            - $ref: '#/components/schemas/Transcript'
            - type: 'null'
        cost_estimate:
          $ref: '#/components/schemas/CostEstimate'
        editable_fields:
          type: array
          items:
            type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        approved_at:
          type:
            - string
            - 'null'
          format: date-time
        video_id:
          type:
            - string
            - 'null'
        session:
          type: object
          properties:
            id:
              type: string
            version:
              type: integer
              minimum: 1
            stage:
              type: string
            approved_at:
              type:
                - string
                - 'null'
              format: date-time
            video_id:
              type:
                - string
                - 'null'
          required:
            - id
            - version
            - stage
          additionalProperties: true
      required:
        - id
        - status
        - source
        - title
        - language
        - script
        - render
        - scenes
        - cost_estimate
        - editable_fields
        - created_at
        - updated_at
    CreateVideoRequest:
      type: object
      properties:
        source:
          $ref: '#/components/schemas/SourceInput'
        title:
          type: string
        language:
          type: string
          default: en-US
        voice:
          $ref: '#/components/schemas/VoiceRef'
        render:
          $ref: '#/components/schemas/RenderOptions'
        webhook_url:
          type: string
          format: uri
        webhook_secret:
          type: string
          description: Optional HMAC secret used to sign outbound Shotmatic webhook deliveries. Signature format is `v1=<hex_hmac_sha256>` over `${timestamp}.${raw_json_body}`.
        metadata:
          type: object
          additionalProperties: true
      required:
        - source
        - voice
        - render
    VideoStatus:
      type: string
      enum:
        - queued
        - planning
        - rendering
        - completed
        - failed
        - cancelled
    Video:
      type: object
      properties:
        id:
          type: string
        status:
          $ref: '#/components/schemas/VideoStatus'
        title:
          type: string
        source_kind:
          type: string
        plan_id:
          type:
            - string
            - 'null'
        voice:
          $ref: '#/components/schemas/VoiceRef'
        render:
          $ref: '#/components/schemas/RenderOptions'
        output_url:
          type:
            - string
            - 'null'
          format: uri
        thumbnail_url:
          type:
            - string
            - 'null'
          format: uri
        transcript:
          anyOf:
            - $ref: '#/components/schemas/Transcript'
            - type: 'null'
        error:
          type:
            - string
            - 'null'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        completed_at:
          type:
            - string
            - 'null'
          format: date-time
      required:
        - id
        - status
        - title
        - source_kind
        - render
        - created_at
        - updated_at
    CreateClipTaskRequest:
      type: object
      properties:
        source_type:
          type: string
          enum:
            - url
        url:
          type: string
          format: uri
        aspect_ratio:
          type: string
          enum:
            - '9:16'
            - '1:1'
            - '16:9'
          default: '9:16'
        number_of_sections:
          type: integer
          minimum: 1
          default: 5
        min_section_duration:
          type: integer
          minimum: 1
          default: 5
        max_section_duration:
          type: integer
          minimum: 1
          default: 60
        generate_voiceover:
          type: boolean
          default: false
        include_background_music:
          type: boolean
          default: false
        language:
          type: string
          default: en-US
        focus_topic:
          type: string
      required:
        - source_type
        - url
    CreateClipTaskUploadRequest:
      type: object
      properties:
        source_type:
          type: string
          enum:
            - upload
        file:
          type: string
          format: binary
        aspect_ratio:
          type: string
          enum:
            - '9:16'
            - '1:1'
            - '16:9'
        number_of_sections:
          type: integer
          minimum: 1
        min_section_duration:
          type: integer
          minimum: 1
        max_section_duration:
          type: integer
          minimum: 1
        generate_voiceover:
          type: boolean
        include_background_music:
          type: boolean
        language:
          type: string
        focus_topic:
          type: string
      required:
        - source_type
        - file
    ClipTaskStatus:
      type: string
      enum:
        - queued
        - processing
        - complete
        - complete_partial
        - failed
    ClipSection:
      type: object
      properties:
        index:
          type: integer
          minimum: 0
        title:
          type:
            - string
            - 'null'
        start_time:
          type: number
        end_time:
          type: number
        duration_seconds:
          type: number
        transcript_excerpt:
          type:
            - string
            - 'null'
        virality_score:
          type:
            - number
            - 'null'
        virality_reason:
          type:
            - string
            - 'null'
      required:
        - index
        - start_time
        - end_time
        - duration_seconds
    ClipTask:
      type: object
      properties:
        id:
          type: string
        status:
          $ref: '#/components/schemas/ClipTaskStatus'
        source_type:
          type: string
          enum:
            - upload
            - url
        source_url:
          type:
            - string
            - 'null'
          format: uri
        source_media_id:
          type:
            - string
            - 'null'
        aspect_ratio:
          type: string
          enum:
            - '9:16'
            - '1:1'
            - '16:9'
        sections:
          type: array
          items:
            $ref: '#/components/schemas/ClipSection'
        transcript:
          anyOf:
            - $ref: '#/components/schemas/Transcript'
            - type: 'null'
        processed_video_url:
          type:
            - string
            - 'null'
          format: uri
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - status
        - source_type
        - aspect_ratio
        - sections
        - created_at
        - updated_at
    RenderClipSelectionRequest:
      type: object
      properties:
        section_index:
          type: integer
          minimum: 0
        section_indices:
          type: array
          items:
            type: integer
            minimum: 0
        title:
          type: string
        use_voice_over:
          type: boolean
          default: false
        voice:
          $ref: '#/components/schemas/VoiceRef'
        aspect_ratio:
          type: string
          enum:
            - '9:16'
            - '1:1'
            - '16:9'
          default: '9:16'
        clip_mode:
          type: string
          enum:
            - standard
            - commentary
          default: standard
        commentary:
          type: object
          properties:
            style:
              type: string
              enum:
                - funny
                - fact_check
                - oppositional
                - educational
                - roast
                - hype
                - react
                - story
                - debate
                - cringe
            avatar_id:
              type: string
            avatar_matted:
              type: boolean
              default: false
            avatar_position:
              type: string
              enum:
                - split
                - overlay
                - corner
                - pip
              default: corner
          additionalProperties: false
      additionalProperties: false
    Workflow:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type:
            - string
            - 'null'
        is_active:
          type: boolean
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - name
        - is_active
        - created_at
        - updated_at
    CreateWorkflowExecutionRequest:
      type: object
      properties:
        inputs:
          type: object
          additionalProperties: true
        start_from_node_id:
          type:
            - string
            - 'null'
        cached_outputs:
          type: object
          additionalProperties: true
      additionalProperties: false
    WorkflowExecutionStatus:
      type: string
      enum:
        - PENDING
        - RUNNING
        - COMPLETED
        - FAILED
        - CANCELLED
    WorkflowExecution:
      type: object
      properties:
        id:
          type: string
        workflow_id:
          type: string
        status:
          $ref: '#/components/schemas/WorkflowExecutionStatus'
        current_node_id:
          type:
            - string
            - 'null'
        state:
          type: object
          additionalProperties: true
        node_stats:
          type: object
          properties:
            total:
              type: integer
            success:
              type: integer
            error:
              type: integer
          required:
            - total
            - success
            - error
        duration_ms:
          type:
            - integer
            - 'null'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - workflow_id
        - status
        - created_at
        - updated_at
    WebhookEventType:
      type: string
      enum:
        - video.queued
        - video.started
        - video.completed
        - video.failed
        - clip_task.completed
        - clip_task.failed
        - workflow_execution.completed
        - workflow_execution.failed
    CreateWebhookRequest:
      type: object
      properties:
        url:
          type: string
          format: uri
        description:
          type: string
          maxLength: 160
        event_types:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            $ref: '#/components/schemas/WebhookEventType'
        events:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            $ref: '#/components/schemas/WebhookEventType'
      required:
        - url
      anyOf:
        - required:
            - event_types
        - required:
            - events
      additionalProperties: false
    WebhookEndpoint:
      type: object
      properties:
        id:
          type: string
          format: uuid
        url:
          type: string
          format: uri
        description:
          type:
            - string
            - 'null'
        status:
          type: string
          enum:
            - active
            - disabled
        event_types:
          type: array
          items:
            $ref: '#/components/schemas/WebhookEventType'
        secret_version:
          type: integer
          minimum: 1
        verified_at:
          type: string
          format: date-time
        disabled_at:
          type:
            - string
            - 'null'
          format: date-time
        last_rotated_at:
          type:
            - string
            - 'null'
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - url
        - status
        - event_types
        - secret_version
        - verified_at
        - created_at
        - updated_at
    WebhookEndpointCreated:
      type: object
      properties:
        endpoint:
          $ref: '#/components/schemas/WebhookEndpoint'
        secret:
          type: string
          description: Returned once. Store it server-side and use it to verify signed deliveries.
      required:
        - endpoint
        - secret
    WebhookEndpointMutationRequest:
      type: object
      properties:
        action:
          const: rotate_secret
        status:
          type: string
          enum:
            - active
            - disabled
      oneOf:
        - required:
            - action
        - required:
            - status
      additionalProperties: false
    WebhookDeliveryStatus:
      type: string
      enum:
        - pending
        - processing
        - retry_due
        - retry_scheduled
        - delivered
        - exhausted
    WebhookDeliverySummary:
      type: object
      properties:
        id:
          type: string
        video_id:
          type: string
        event_type:
          $ref: '#/components/schemas/WebhookEventType'
        status:
          $ref: '#/components/schemas/WebhookDeliveryStatus'
        target_url:
          type: string
          format: uri
        attempt_count:
          type: integer
          minimum: 0
        response_status:
          type:
            - integer
            - 'null'
        last_error:
          type:
            - string
            - 'null'
        last_attempt_at:
          type:
            - string
            - 'null'
          format: date-time
        next_attempt_at:
          type:
            - string
            - 'null'
          format: date-time
        processing_at:
          type:
            - string
            - 'null'
          format: date-time
        delivered_at:
          type:
            - string
            - 'null'
          format: date-time
        failed_at:
          type:
            - string
            - 'null'
          format: date-time
        exhausted_at:
          type:
            - string
            - 'null'
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
      required:
        - id
        - video_id
        - event_type
        - status
        - target_url
        - attempt_count
        - created_at
        - updated_at
    WebhookDeliveryDetail:
      allOf:
        - $ref: '#/components/schemas/WebhookDeliverySummary'
        - type: object
          properties:
            payload:
              type: object
              additionalProperties: true
            response_body:
              type:
                - string
                - 'null'
            dedupe_key:
              type: string
            processing_by:
              type:
                - string
                - 'null'
          required:
            - payload
            - dedupe_key
    WebhookDeliveryCollection:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/WebhookDeliverySummary'
        pagination:
          $ref: '#/components/schemas/Pagination'
      required:
        - data
        - pagination
    UsageSummary:
      type: object
      properties:
        credits_remaining:
          type: integer
          minimum: 0
        active_sequences:
          type: integer
          minimum: 0
        sequence_quota:
          type: integer
          minimum: 0
        max_frequency_per_week:
          type: integer
          minimum: 0
        videos_created_this_period:
          type: integer
          minimum: 0
        clip_tasks_created_this_period:
          type: integer
          minimum: 0
      required:
        - credits_remaining
        - active_sequences
        - sequence_quota
        - max_frequency_per_week
        - videos_created_this_period
        - clip_tasks_created_this_period
    OrderedMediaExternalReferenceV1:
      type: object
      additionalProperties: false
      properties:
        system:
          type: string
          minLength: 1
          maxLength: 120
        id:
          type: string
          minLength: 1
          maxLength: 120
        revision:
          type: string
          pattern: ^sha256:[a-f0-9]{64}$
      required:
        - system
        - id
        - revision
    OrderedMediaImportRequestV1:
      type: object
      additionalProperties: false
      properties:
        purpose:
          const: ordered_media.v1
        source_url:
          type: string
          format: uri
        kind:
          const: video
        expected:
          type: object
          additionalProperties: false
          properties:
            sha256:
              type: string
              pattern: ^[a-f0-9]{64}$
            max_bytes:
              type: integer
              minimum: 1
              maximum: 125829120
          required:
            - max_bytes
        producer_asset_reference:
          $ref: '#/components/schemas/OrderedMediaExternalReferenceV1'
      required:
        - purpose
        - source_url
        - kind
        - expected
        - producer_asset_reference
    OrderedMediaImportV1:
      type: object
      additionalProperties: true
      properties:
        id:
          type: string
          format: uuid
        object:
          const: media_import
        purpose:
          const: ordered_media.v1
        state:
          type: string
          enum:
            - queued
            - fetching
            - probing
            - ready
            - failed
        producer_asset_reference:
          $ref: '#/components/schemas/OrderedMediaExternalReferenceV1'
        actual_sha256:
          type:
            - string
            - 'null'
          pattern: ^[a-f0-9]{64}$
        media_id:
          type:
            - string
            - 'null'
          format: uuid
        probe:
          type: object
          additionalProperties: true
        retryable:
          type: boolean
        replayed:
          type: boolean
      required:
        - id
        - object
        - purpose
        - state
        - producer_asset_reference
        - probe
        - retryable
        - replayed
    OrderedMediaSessionRequestV1:
      type: object
      additionalProperties: false
      description: Deterministic producer-authored composition. Availability is environment and key gated.
      x-fixture: docs/fixtures/ordered-media/v1/neutral-product-demo.request.json
      properties:
        source:
          type: object
          additionalProperties: false
          properties:
            kind:
              const: workflow_profile_binding
            binding_id:
              type: string
              format: uuid
            binding_version:
              type: string
              minLength: 1
            creative_package:
              type: object
              additionalProperties: false
              properties:
                assets:
                  type: array
                  minItems: 1
                  maxItems: 30
                  items:
                    $ref: '#/components/schemas/OrderedMediaAssetV1'
                composition:
                  $ref: '#/components/schemas/OrderedMediaCompositionV1'
                render:
                  type: object
                  additionalProperties: false
                  properties:
                    aspect_ratio:
                      type: string
                      enum:
                        - '9:16'
                        - '16:9'
                        - '1:1'
                  required:
                    - aspect_ratio
              required:
                - assets
                - composition
                - render
          required:
            - kind
            - binding_id
            - binding_version
            - creative_package
        goal:
          type: object
          additionalProperties: false
          properties:
            title:
              type: string
              minLength: 1
              maxLength: 160
            target_platform:
              type: string
              minLength: 1
              maxLength: 80
          required:
            - title
        mode:
          const: asset_first
        external_reference:
          $ref: '#/components/schemas/OrderedMediaExternalReferenceV1'
      required:
        - source
        - goal
        - mode
        - external_reference
    OrderedMediaSessionV1:
      type: object
      additionalProperties: true
      properties:
        id:
          type: string
          format: uuid
        object:
          const: creation_session
        status:
          const: ready_for_review
        session_version:
          type: integer
          minimum: 1
        artifact_set_sha256:
          type: string
          pattern: ^sha256:[a-f0-9]{64}$
        external_reference:
          $ref: '#/components/schemas/OrderedMediaExternalReferenceV1'
        contract_version:
          const: ordered_media.v1
        compiler_version:
          const: ordered_media.v1
        estimate:
          $ref: '#/components/schemas/OrderedMediaEstimateV1'
        preview_bundle:
          type: object
          additionalProperties: true
        replayed:
          type: boolean
      required:
        - id
        - object
        - status
        - session_version
        - artifact_set_sha256
        - external_reference
        - contract_version
        - compiler_version
        - estimate
        - preview_bundle
        - replayed
    OrderedMediaApprovalRequestV1:
      type: object
      additionalProperties: false
      properties:
        expected_session_version:
          type: integer
          minimum: 1
        expected_artifact_set_sha256:
          type: string
          pattern: ^sha256:[a-f0-9]{64}$
        expected_estimate_id:
          type: string
          minLength: 1
        webhook_endpoint_id:
          type: string
          format: uuid
      required:
        - expected_session_version
        - expected_artifact_set_sha256
        - expected_estimate_id
    OrderedMediaApprovalV1:
      type: object
      additionalProperties: true
      properties:
        id:
          type: string
          format: uuid
        object:
          const: creation_session
        status:
          const: approved
        video_id:
          type: string
          format: uuid
        render_run_id:
          type: string
          format: uuid
        usage_receipt_id:
          type: string
          format: uuid
        reservation:
          type: object
          additionalProperties: false
          properties:
            operation_id:
              type: string
              format: uuid
            status:
              type: string
            amount:
              type: integer
              minimum: 0
            unit:
              const: credits
          required:
            - operation_id
            - status
            - amount
            - unit
        idempotent:
          type: boolean
      required:
        - id
        - object
        - status
        - video_id
        - render_run_id
        - usage_receipt_id
        - reservation
        - idempotent
    OrderedMediaBranchRequestV1:
      type: object
      additionalProperties: false
      properties:
        expected_base_session_version:
          type: integer
          minimum: 1
        expected_base_artifact_set_sha256:
          type: string
          pattern: ^sha256:[a-f0-9]{64}$
        external_reference:
          $ref: '#/components/schemas/OrderedMediaExternalReferenceV1'
        render:
          type: object
          additionalProperties: false
          properties:
            aspect_ratio:
              type: string
              enum:
                - '9:16'
                - '16:9'
                - '1:1'
          required:
            - aspect_ratio
        segment_framing_overrides:
          type: array
          items:
            type: object
            additionalProperties: false
            properties:
              segment_id:
                type: string
              framing_hint:
                $ref: '#/components/schemas/OrderedMediaFramingHintV1'
            required:
              - segment_id
              - framing_hint
      required:
        - expected_base_session_version
        - expected_base_artifact_set_sha256
        - external_reference
        - render
        - segment_framing_overrides
    OrderedMediaUsageReceiptV1:
      type: object
      additionalProperties: true
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          enum:
            - captured
            - released
        unit:
          const: credits
        amounts:
          type: object
          additionalProperties: false
          properties:
            reserved:
              type: integer
              minimum: 0
            captured:
              type: integer
              minimum: 0
            released:
              type: integer
              minimum: 0
            net:
              type: integer
              minimum: 0
          required:
            - reserved
            - captured
            - released
            - net
      required:
        - id
        - status
        - unit
        - amounts
    AvatarIdentityConsent:
      type: object
      description: Safe account projection of immutable Identity-level avatar consent. Evidence references and hashes are never returned.
      additionalProperties: false
      properties:
        object:
          const: avatar_identity_consent
        id:
          type: string
          format: uuid
        identity_id:
          type: string
          format: uuid
        subject_kind:
          type: string
          enum:
            - synthetic
            - selfie
            - licensed
        state:
          type: string
          enum:
            - active
            - revoked
        scope:
          type: array
          items:
            type: string
            enum:
              - avatar_training
              - avatar_generation
        consented_at:
          type: string
          format: date-time
        expires_at:
          type:
            - string
            - 'null'
          format: date-time
        revoked_at:
          type:
            - string
            - 'null'
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - identity_id
        - subject_kind
        - state
        - scope
        - consented_at
        - expires_at
        - revoked_at
        - created_at
        - updated_at
        - links
    RevokeAvatarIdentityConsentRequest:
      type: object
      additionalProperties: false
      properties:
        consent_id:
          type: string
          format: uuid
        reason:
          type: string
          minLength: 3
          maxLength: 500
      required:
        - consent_id
        - reason
    AvatarProfileAvailability:
      type: object
      description: Provider-free evaluation of whether this Profile can satisfy the exact picker request and whether an immutable Performance is already cached.
      additionalProperties: false
      properties:
        state:
          type: string
          enum:
            - ready_cached
            - ready_to_generate
            - setup_required
            - consent_required
            - provider_consent_required
            - strategy_incompatible
            - temporarily_unavailable
        selectable:
          type: boolean
        requiresPaidProviderWork:
          type: boolean
        action:
          type: string
          enum:
            - create
            - review_quote
            - review_setup
            - complete_consent
            - complete_provider_consent
            - change_strategy
            - retry_later
        reasonCodes:
          type: array
          items:
            type: string
            enum:
              - profile_unavailable
              - provider_unavailable
              - consent_not_active
              - provider_consent_not_approved
              - binding_not_ready
              - voice_binding_not_ready
              - voice_unavailable
              - aspect_ratio_not_supported
              - duration_not_supported
              - locale_not_supported
              - cutout_not_supported
      required:
        - state
        - selectable
        - requiresPaidProviderWork
        - action
        - reasonCodes
    AvatarProfile:
      type: object
      description: Provider-neutral account avatar handle. Provider bindings remain private to Shotmatic.
      properties:
        object:
          const: avatar_profile
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type:
            - string
            - 'null'
        status:
          type: string
          enum:
            - draft
            - provisioning
            - ready
            - disabled
            - failed
        origin:
          type: string
          enum:
            - account
            - featured
        billing_class:
          type: string
          enum:
            - ugc
            - avatar_talking_photo
          description: Stable Shotmatic billing category for this provider-neutral Profile.
        default_strategy:
          type: string
          enum:
            - bookends
            - floating_host
            - split_host
            - presenter_first
            - scene_cameos
            - full_presenter
        default_motion_base_id:
          type:
            - string
            - 'null'
          format: uuid
          description: Qualified provider-neutral base-motion video selected by default for existing-video lip-sync workflows.
        poster_media_id:
          type:
            - string
            - 'null'
          format: uuid
        preview_media_id:
          type:
            - string
            - 'null'
          format: uuid
        poster_url:
          type:
            - string
            - 'null'
          format: uri
          description: Short-lived URL for an account-owned private-R2 poster. Never use it as cache identity.
        default_voice:
          oneOf:
            - type: object
              additionalProperties: false
              properties:
                id:
                  type: string
                  format: uuid
                name:
                  type: string
                locale:
                  type:
                    - string
                    - 'null'
              required:
                - id
                - name
                - locale
            - type: 'null'
          description: Provider-neutral account Voice selected by default for this Profile. Provider Voice IDs and bindings are never exposed.
        capabilities:
          type: array
          items:
            type: string
        availability:
          $ref: '#/components/schemas/AvatarProfileAvailability'
        last_used_at:
          type:
            - string
            - 'null'
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - name
        - description
        - status
        - origin
        - billing_class
        - default_strategy
        - default_motion_base_id
        - poster_media_id
        - preview_media_id
        - poster_url
        - default_voice
        - capabilities
        - availability
        - last_used_at
        - created_at
        - updated_at
        - links
      additionalProperties: false
    AvatarProfileCollection:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/AvatarProfile'
      required:
        - data
      additionalProperties: false
    AvatarProfileVoiceAssignmentRequest:
      type: object
      additionalProperties: false
      properties:
        default_voice_identity_id:
          type: string
          format: uuid
      required:
        - default_voice_identity_id
    AvatarMotionBase:
      type: object
      additionalProperties: false
      description: Immutable qualified private-R2 base-motion video tied to one account-owned Avatar Identity and Look. Provider resources and consent evidence stay service-only.
      properties:
        object:
          const: avatar_motion_base
        id:
          type: string
          format: uuid
        profile_id:
          type: string
          format: uuid
        identity_id:
          type: string
          format: uuid
        look_id:
          type: string
          format: uuid
        source_pack_id:
          type: string
          format: uuid
        name:
          type: string
        state:
          type: string
          enum:
            - ready
            - disabled
            - failed
        source_media_id:
          type: string
          format: uuid
        source_sha256:
          type: string
          pattern: ^[a-f0-9]{64}$
        duration_ms:
          type: integer
          minimum: 1
          maximum: 300000
        content_type:
          const: video/mp4
        qualification_report:
          type: object
          properties:
            contract_version:
              const: avatar_motion_base_qualification.v1
            verified:
              const: true
          required:
            - contract_version
            - verified
          additionalProperties: true
        provenance:
          type: object
          additionalProperties: true
        rights_scope:
          type: array
          minItems: 1
          items:
            type: string
            enum:
              - avatar_training
              - avatar_generation
        last_used_at:
          type:
            - string
            - 'null'
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - profile_id
        - identity_id
        - look_id
        - source_pack_id
        - name
        - state
        - source_media_id
        - source_sha256
        - duration_ms
        - content_type
        - qualification_report
        - provenance
        - rights_scope
        - last_used_at
        - created_at
        - updated_at
        - links
    AvatarMotionBaseCollection:
      type: object
      additionalProperties: false
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/AvatarMotionBase'
      required:
        - data
    CreateAvatarMotionBaseRequest:
      type: object
      additionalProperties: false
      description: Registers only a deliberately selected Motion Reference from the Profile Identity's qualified Source Pack. Arbitrary media and provider resource identifiers are not accepted.
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 120
        look_id:
          type: string
          format: uuid
        source_asset_id:
          type: string
          format: uuid
        primary_subject_confirmed:
          const: true
        set_as_default:
          type: boolean
          default: false
      required:
        - name
        - look_id
        - source_asset_id
        - primary_subject_confirmed
    CreateAvatarLipSyncQuoteRequest:
      type: object
      description: Provider-neutral controls for synchronizing one qualified Motion Base with one immutable account-owned private-R2 audio asset.
      additionalProperties: false
      properties:
        audio_media_id:
          type: string
          format: uuid
        duration_mode:
          type: string
          enum:
            - cut_off
            - loop
            - bounce
            - silence
            - remap
          default: cut_off
        speaker:
          oneOf:
            - type: object
              additionalProperties: false
              properties:
                mode:
                  const: single_face
              required:
                - mode
            - type: object
              additionalProperties: false
              properties:
                mode:
                  const: auto_active_speaker
              required:
                - mode
            - type: object
              additionalProperties: false
              properties:
                mode:
                  const: manual_point
                frame_number:
                  type: integer
                  minimum: 0
                coordinates:
                  type: array
                  minItems: 2
                  maxItems: 2
                  items:
                    type: number
                    minimum: 0
                    maximum: 1
              required:
                - mode
                - frame_number
                - coordinates
          default:
            mode: single_face
        output_format:
          type: string
          enum:
            - mp4
            - webm
          default: mp4
        preserve_source_audio:
          type: boolean
          default: false
        quality_tier:
          type: string
          enum:
            - standard
            - premium
          default: standard
      required:
        - audio_media_id
    AvatarLipSyncJob:
      type: object
      description: Durable provider-neutral state of one explicitly approved Motion Base lip-sync transform or a cache hit.
      additionalProperties: false
      properties:
        object:
          const: avatar_lip_sync_job
        id:
          type: string
          format: uuid
        profile_id:
          type: string
          format: uuid
        motion_base_id:
          type: string
          format: uuid
        state:
          type: string
          enum:
            - requested
            - processing
            - ready
            - failed
            - cancelled
            - unknown_acceptance
        source:
          const: cache
        provider_submissions:
          const: 0
        output_media_id:
          type:
            - string
            - 'null'
          format: uuid
        duration_ms:
          type:
            - integer
            - 'null'
          minimum: 1
        error:
          type: object
          additionalProperties: false
          properties:
            code:
              type: string
          required:
            - code
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - profile_id
        - motion_base_id
        - state
        - output_media_id
        - duration_ms
        - links
    AvatarLipSyncQuote:
      type: object
      description: Safe account projection of one frozen lip-sync quote. Provider routing, raw estimates, signed URLs, and storage details remain service-only.
      additionalProperties: false
      properties:
        object:
          const: avatar_lip_sync_quote
        id:
          type: string
          format: uuid
        profile_id:
          type: string
          format: uuid
        motion_base_id:
          type: string
          format: uuid
        state:
          type: string
          enum:
            - quoted
            - approved
            - expired
        amount:
          type: integer
          minimum: 0
        currency:
          const: credits
        pricing_version:
          type: string
        lip_sync_job_id:
          type:
            - string
            - 'null'
          format: uuid
        expires_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        credits_reserved:
          type: boolean
        idempotent:
          type: boolean
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - profile_id
        - motion_base_id
        - state
        - amount
        - currency
        - pricing_version
        - lip_sync_job_id
        - expires_at
        - created_at
        - updated_at
        - links
    ApproveAvatarLipSyncQuoteRequest:
      type: object
      additionalProperties: false
      properties:
        expected_amount:
          type: integer
          minimum: 0
        expected_pricing_version:
          type: string
          minLength: 1
          maxLength: 100
          pattern: ^[a-z0-9_.-]+$
      required:
        - expected_amount
        - expected_pricing_version
    AvatarProviderConsent:
      type: object
      description: Safe account projection of the provider-specific Digital Twin approval gate. Provider identifiers and immutable evidence remain service-only.
      additionalProperties: false
      properties:
        object:
          const: avatar_provider_consent
        id:
          type: string
          format: uuid
        profile_id:
          type: string
          format: uuid
        identity_id:
          type: string
          format: uuid
        state:
          type: string
          enum:
            - required
            - submitting
            - pending
            - approved
            - rejected
            - expired
            - failed
            - unknown_acceptance
        completion_url:
          type:
            - string
            - 'null'
          format: uri
          description: Short-lived external browser approval URL. Never cache or persist this as an identity.
        completion_url_expires_at:
          type:
            - string
            - 'null'
          format: date-time
        approved_at:
          type:
            - string
            - 'null'
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - profile_id
        - identity_id
        - state
        - completion_url
        - completion_url_expires_at
        - approved_at
        - created_at
        - updated_at
        - links
    CreateAvatarPerformanceRequest:
      type: object
      description: Provider-neutral speech performance input. Presentation strategy and layout are compiled separately into avatar_track.v1.
      additionalProperties: false
      properties:
        audio_media_id:
          type: string
          format: uuid
          description: Account-owned immutable private-R2 audio media.
        locale:
          type: string
          minLength: 2
          maxLength: 35
          examples:
            - en-US
        aspect_ratio:
          type: string
          enum:
            - '9:16'
            - '16:9'
            - '1:1'
            - '4:5'
          default: '9:16'
        output:
          type: object
          additionalProperties: false
          properties:
            background:
              type: string
              enum:
                - transparent
                - solid
                - source
            format:
              type: string
              enum:
                - webm
                - mp4
          required:
            - background
            - format
        performance:
          type: object
          additionalProperties: false
          properties:
            preset:
              type: string
              enum:
                - neutral
                - explainer
                - expressive
            motion_prompt:
              type: string
              minLength: 1
              maxLength: 500
            expressiveness:
              type: number
              minimum: 0
              maximum: 1
          required:
            - preset
        quality_tier:
          type: string
          enum:
            - draft
            - standard
            - premium
            - reuse
          default: standard
        fallback:
          type: string
          enum:
            - none
            - same_identity_same_tier
            - allow_lower_tier
          default: none
      required:
        - audio_media_id
        - locale
        - output
        - performance
    AvatarPerformance:
      type: object
      description: Durable provider-neutral speaking media reusable across Avatar Track placements.
      additionalProperties: false
      properties:
        object:
          const: avatar_performance
        id:
          type: string
          format: uuid
        profile_id:
          type: string
          format: uuid
        state:
          type: string
          enum:
            - queued
            - processing
            - reconciling
            - ready
            - failed
            - cancelled
        output_media_id:
          type:
            - string
            - 'null'
          format: uuid
        duration_ms:
          type:
            - integer
            - 'null'
          minimum: 1
        source:
          type: string
          enum:
            - cache
            - fixture
            - provider
          description: Shotmatic execution source; never a vendor identifier.
        provider_submissions:
          type: integer
          minimum: 0
          maximum: 1
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - profile_id
        - state
        - output_media_id
        - duration_ms
        - links
    AvatarPerformanceQuote:
      type: object
      description: Safe account projection of a frozen Avatar Performance price and approval state. Provider routing, raw estimates, and storage details remain service-only.
      additionalProperties: false
      properties:
        object:
          const: avatar_performance_quote
        id:
          type: string
          format: uuid
        profile_id:
          type: string
          format: uuid
        state:
          type: string
          enum:
            - quoted
            - approved
            - expired
        amount:
          type: integer
          minimum: 0
        currency:
          const: credits
        pricing_version:
          type: string
        performance_id:
          type:
            - string
            - 'null'
          format: uuid
        expires_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        credits_reserved:
          type: boolean
        idempotent:
          type: boolean
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - profile_id
        - state
        - amount
        - currency
        - pricing_version
        - performance_id
        - expires_at
        - created_at
        - updated_at
        - links
    ApproveAvatarPerformanceQuoteRequest:
      type: object
      additionalProperties: false
      properties:
        expected_amount:
          type: integer
          minimum: 0
        expected_pricing_version:
          type: string
          minLength: 1
          maxLength: 100
          pattern: ^[a-z0-9_.-]+$
      required:
        - expected_amount
        - expected_pricing_version
    CreateAvatarVideoMattingQuoteRequest:
      type: object
      description: Provider-neutral cached cutout policy for one account-owned immutable opaque Avatar Performance.
      additionalProperties: false
      properties:
        output_resolution:
          type: string
          enum:
            - source
            - 720p
            - 1080p
          default: source
        temporal_consistency:
          type: string
          enum:
            - standard
            - high
          default: standard
        edge_detail:
          type: string
          enum:
            - standard
            - hair
          default: standard
        quality_tier:
          type: string
          enum:
            - standard
            - premium
          default: standard
    AvatarMattingDerivative:
      type: object
      description: Reused private-R2 VP9 WebM cutout whose transparency was decoded and verified by Shotmatic.
      additionalProperties: false
      properties:
        object:
          const: avatar_matting_derivative
        id:
          type: string
          format: uuid
        profile_id:
          type: string
          format: uuid
        performance_id:
          type: string
          format: uuid
        state:
          const: ready
        source:
          const: cache
        provider_submissions:
          const: 0
        output_media_id:
          type: string
          format: uuid
        duration_ms:
          type: integer
          minimum: 1
        alpha_verified:
          const: true
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - profile_id
        - performance_id
        - state
        - source
        - provider_submissions
        - output_media_id
        - duration_ms
        - alpha_verified
        - links
    AvatarVideoMattingQuote:
      type: object
      description: Safe account projection of an optional VideoMatting transform quote. Provider routing, raw estimates, signed URLs, and storage details remain service-only.
      additionalProperties: false
      properties:
        object:
          const: avatar_video_matting_quote
        id:
          type: string
          format: uuid
        profile_id:
          type: string
          format: uuid
        performance_id:
          type: string
          format: uuid
        state:
          type: string
          enum:
            - quoted
            - approved
            - expired
        amount:
          type: integer
          minimum: 0
        currency:
          const: credits
        pricing_version:
          type: string
        matting_job_id:
          type:
            - string
            - 'null'
          format: uuid
        expires_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        credits_reserved:
          type: boolean
        idempotent:
          type: boolean
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - profile_id
        - performance_id
        - state
        - amount
        - currency
        - pricing_version
        - matting_job_id
        - expires_at
        - created_at
        - updated_at
        - links
    ApproveAvatarVideoMattingQuoteRequest:
      type: object
      additionalProperties: false
      properties:
        expected_amount:
          type: integer
          minimum: 0
        expected_pricing_version:
          type: string
          minLength: 1
          maxLength: 100
          pattern: ^[a-z0-9_.-]+$
      required:
        - expected_amount
        - expected_pricing_version
    AvatarVideoMattingJob:
      type: object
      description: Durable provider-neutral state of one explicitly approved cached VideoMatting transform.
      additionalProperties: false
      properties:
        object:
          const: avatar_video_matting_job
        id:
          type: string
          format: uuid
        profile_id:
          type: string
          format: uuid
        performance_id:
          type: string
          format: uuid
        state:
          type: string
          enum:
            - requested
            - processing
            - ready
            - failed
            - cancelled
            - unknown_acceptance
        output_media_id:
          type:
            - string
            - 'null'
          format: uuid
        duration_ms:
          type: integer
          minimum: 1
        alpha_verified:
          type: boolean
        error:
          type: object
          additionalProperties: false
          properties:
            code:
              type: string
          required:
            - code
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - profile_id
        - performance_id
        - state
        - output_media_id
        - duration_ms
        - alpha_verified
        - created_at
        - updated_at
        - links
    AvatarSourcePack:
      type: object
      additionalProperties: false
      description: Provider-neutral source collection. Provider bindings and vendor resource IDs remain private to Shotmatic.
      properties:
        object:
          const: avatar_source_pack
        id:
          type: string
          format: uuid
        identity_id:
          type: string
          format: uuid
        name:
          type: string
        state:
          type: string
          enum:
            - draft
            - acquiring
            - candidates_ready
            - primary_selected
            - qualifying
            - ready_for_binding
            - needs_input
            - ready
            - rejected
            - failed
            - archived
        acquisition_kind:
          type: string
          enum:
            - upload
            - generated
            - derived
            - provider_catalog
        primary_media_id:
          type:
            - string
            - 'null'
          format: uuid
        rights_scope:
          type: array
          items:
            type: string
        provenance:
          type: object
          additionalProperties: true
        quality_report:
          type: object
          additionalProperties: true
        candidate_count:
          type: integer
          minimum: 0
        selected_asset_count:
          type: integer
          minimum: 0
        cleanup_pending:
          type: boolean
          description: True only when primary selection succeeded but asynchronous quarantine cleanup still needs to retry.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - identity_id
        - name
        - state
        - acquisition_kind
        - primary_media_id
        - rights_scope
        - provenance
        - quality_report
        - candidate_count
        - selected_asset_count
        - created_at
        - updated_at
        - links
    AvatarSourcePackCollection:
      type: object
      additionalProperties: false
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/AvatarSourcePack'
      required:
        - data
    CreateAvatarSourcePackRequest:
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 120
        subject_kind:
          type: string
          enum:
            - synthetic
            - selfie
            - licensed
        acquisition_kind:
          type: string
          enum:
            - upload
            - generated
            - derived
        provenance:
          type: object
          additionalProperties: true
        rights_attestation:
          type: object
          additionalProperties: false
          description: Immutable account attestation for avatar generation. Provider training requires a separate consent operation.
          properties:
            accepted:
              const: true
            attestation_version:
              const: avatar-source-rights.v1
            consented_at:
              type: string
              format: date-time
            expires_at:
              type:
                - string
                - 'null'
              format: date-time
              description: Required for licensed subjects.
            permission_reference:
              type: string
              minLength: 1
              maxLength: 500
              description: Required for licensed subjects. Use an opaque contract, release, or internal evidence identifier; never a signed URL.
          required:
            - accepted
            - attestation_version
            - consented_at
      required:
        - name
        - subject_kind
        - acquisition_kind
        - rights_attestation
    CreateAvatarDigitalTwinSourcePackRequest:
      type: object
      additionalProperties: false
      description: Atomically creates one trained identity from one account-owned private-R2 training video plus a different immutable consent-evidence video. This intake performs no provider call and reserves no credits.
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 120
        training_video_media_id:
          type: string
          format: uuid
          description: Completed private-R2 video media ID used only as training evidence.
        consent_evidence_media_id:
          type: string
          format: uuid
          description: Completed private-R2 video media ID for a separate recorded consent statement.
        rights_basis:
          type: string
          enum:
            - self
            - licensed
        rights_attestation:
          type: object
          additionalProperties: false
          properties:
            accepted:
              const: true
            attestation_version:
              const: avatar-digital-twin-rights.v1
            consented_at:
              type: string
              format: date-time
            expires_at:
              type: string
              format: date-time
              description: Required when rights_basis is licensed.
            permission_reference:
              type: string
              minLength: 1
              maxLength: 500
              description: Required when rights_basis is licensed. Use an opaque agreement reference.
          required:
            - accepted
            - attestation_version
            - consented_at
        provenance:
          type: object
          additionalProperties: true
      required:
        - name
        - training_video_media_id
        - consent_evidence_media_id
        - rights_basis
        - rights_attestation
    AvatarSourceCandidate:
      type: object
      additionalProperties: false
      properties:
        object:
          const: avatar_source_candidate
        id:
          type: string
          format: uuid
        source_pack_id:
          type: string
          format: uuid
        media_id:
          type: string
          format: uuid
        media_sha256:
          type: string
          pattern: ^[0-9a-f]{64}$
        acquisition_kind:
          type: string
          enum:
            - upload
            - generated
            - derived
            - provider_catalog
        status:
          type: string
          enum:
            - candidate
            - selected
            - rejected
        quality_report:
          type: object
          additionalProperties: true
        provenance:
          type: object
          additionalProperties: true
        retention_expires_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - source_pack_id
        - media_id
        - media_sha256
        - acquisition_kind
        - status
        - quality_report
        - provenance
        - retention_expires_at
        - created_at
        - updated_at
        - links
    AvatarSourceCandidateCollection:
      type: object
      additionalProperties: false
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/AvatarSourceCandidate'
      required:
        - data
    AddAvatarSourceCandidateRequest:
      type: object
      additionalProperties: false
      properties:
        media_id:
          type: string
          format: uuid
        provenance:
          type: object
          additionalProperties: true
      required:
        - media_id
    SelectAvatarSourcePrimaryRequest:
      type: object
      additionalProperties: false
      properties:
        candidate_id:
          type: string
          format: uuid
      required:
        - candidate_id
    AvatarSourceQualification:
      type: object
      additionalProperties: false
      description: Safe projection of a provider-free private-R2 qualification run. Worker leases, input storage keys, and provider state are never public.
      properties:
        object:
          const: avatar_source_qualification
        id:
          type: string
          format: uuid
        source_pack_id:
          type: string
          format: uuid
        state:
          type: string
          enum:
            - queued
            - processing
            - ready_for_binding
            - needs_input
            - rejected
            - failed
            - cancelled
        quality_tier:
          type: string
          enum:
            - draft
            - standard
            - premium
        ruleset_version:
          type: string
        report:
          type: object
          additionalProperties: true
        error_code:
          type:
            - string
            - 'null'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        completed_at:
          type:
            - string
            - 'null'
          format: date-time
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - source_pack_id
        - state
        - quality_tier
        - ruleset_version
        - report
        - error_code
        - created_at
        - updated_at
        - completed_at
        - links
    AvatarSourceQualificationRequest:
      type: object
      additionalProperties: false
      properties:
        quality_tier:
          type: string
          enum:
            - draft
            - standard
            - premium
          default: standard
        primary_subject_confirmed:
          const: true
          description: Confirms the account deliberately selected the visible subject. Qualification never substitutes another candidate.
      required:
        - primary_subject_confirmed
    AvatarProfileProvisioningJob:
      type: object
      additionalProperties: false
      description: Safe Shotmatic job projection. Provider names, vendor resource IDs, commands, estimates, leases, and attempts remain private.
      properties:
        object:
          const: avatar_profile_provisioning_job
        id:
          type: string
          format: uuid
        source_pack_id:
          type: string
          format: uuid
        state:
          type: string
          enum:
            - queued
            - submitting
            - processing
            - succeeded
            - failed
            - cancelled
            - unknown_acceptance
        recipe:
          type: string
          enum:
            - reusable_photo_avatar
            - prompt_avatar
            - digital_twin
        profile_name:
          type: string
        default_strategy:
          type: string
          enum:
            - bookends
            - floating_host
            - split_host
            - presenter_first
            - scene_cameos
            - full_presenter
        profile_id:
          type:
            - string
            - 'null'
          format: uuid
        error_code:
          type:
            - string
            - 'null'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        completed_at:
          type:
            - string
            - 'null'
          format: date-time
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - source_pack_id
        - state
        - recipe
        - profile_name
        - default_strategy
        - profile_id
        - error_code
        - created_at
        - updated_at
        - completed_at
        - links
    AvatarProfileProvisioningRequest:
      description: Provider-neutral request to turn a qualified Source Pack into a reusable quick-selection Avatar Profile. Clients never choose a vendor.
      oneOf:
        - type: object
          additionalProperties: false
          properties:
            recipe:
              const: reusable_photo_avatar
            profile_name:
              type: string
              minLength: 1
              maxLength: 120
            default_strategy:
              type: string
              enum:
                - bookends
                - floating_host
                - split_host
                - presenter_first
                - scene_cameos
                - full_presenter
            quality_tier:
              type: string
              enum:
                - draft
                - standard
                - premium
              default: standard
          required:
            - recipe
            - profile_name
            - default_strategy
        - type: object
          additionalProperties: false
          properties:
            recipe:
              const: prompt_avatar
            profile_name:
              type: string
              minLength: 1
              maxLength: 120
            default_strategy:
              type: string
              enum:
                - bookends
                - floating_host
                - split_host
                - presenter_first
                - scene_cameos
                - full_presenter
            quality_tier:
              type: string
              enum:
                - draft
                - standard
                - premium
              default: standard
            prompt:
              type: object
              additionalProperties: false
              properties:
                positive:
                  type: string
                  minLength: 1
                  maxLength: 1000
                negative:
                  type: string
                  minLength: 1
                  maxLength: 1000
              required:
                - positive
            reference_profile_id:
              type: string
              format: uuid
          required:
            - recipe
            - profile_name
            - default_strategy
            - prompt
            - reference_profile_id
        - type: object
          additionalProperties: false
          properties:
            recipe:
              const: digital_twin
            profile_name:
              type: string
              minLength: 1
              maxLength: 120
            default_strategy:
              type: string
              enum:
                - bookends
                - floating_host
                - split_host
                - presenter_first
                - scene_cameos
                - full_presenter
            quality_tier:
              type: string
              enum:
                - draft
                - standard
                - premium
              default: standard
          required:
            - recipe
            - profile_name
            - default_strategy
    AvatarProfileProvisioningQuote:
      type: object
      additionalProperties: false
      description: Safe account-facing Profile creation quote. Provider names, adapter versions, raw estimates, provider costs, commands, and provider resource IDs remain service-only.
      properties:
        object:
          const: avatar_profile_provisioning_quote
        id:
          type: string
          format: uuid
        source_pack_id:
          type: string
          format: uuid
        state:
          type: string
          enum:
            - quoted
            - approved
            - expired
        amount:
          type: integer
          minimum: 0
        currency:
          const: credits
        pricing_version:
          type: string
        request:
          $ref: '#/components/schemas/AvatarProfileProvisioningRequest'
        job_id:
          type:
            - string
            - 'null'
          format: uuid
        credits_reserved:
          type: boolean
        idempotent:
          type: boolean
        expires_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - source_pack_id
        - state
        - amount
        - currency
        - pricing_version
        - job_id
        - expires_at
        - created_at
        - updated_at
        - links
    AvatarProfileProvisioningApprovalRequest:
      type: object
      additionalProperties: false
      properties:
        expected_amount:
          type: integer
          minimum: 0
        expected_pricing_version:
          type: string
          minLength: 1
          maxLength: 100
      required:
        - expected_amount
        - expected_pricing_version
    AvatarSourceAcquisitionSourceAsset:
      type: object
      additionalProperties: false
      properties:
        media_id:
          type: string
          format: uuid
        role:
          type: string
          enum:
            - identity_anchor
            - style_reference
            - pose_reference
      required:
        - media_id
        - role
    AvatarSourceAcquisitionQuoteRequest:
      type: object
      additionalProperties: false
      description: Provider-neutral request for a costed candidate-image collection. Provider selection remains private to Shotmatic. Prompt-only collections accept no source assets. Reference-driven collections require unique media IDs registered in this Source Pack and exactly one identity anchor.
      properties:
        recipe:
          type: string
          enum:
            - generated_portrait_collection
            - derived_look_collection
        prompt:
          type: object
          additionalProperties: false
          properties:
            positive:
              type: string
              minLength: 1
              maxLength: 2000
            negative:
              type: string
              minLength: 1
              maxLength: 1000
            policy_hash:
              type: string
              pattern: ^sha256:[0-9a-f]{64}$
          required:
            - positive
            - policy_hash
        source_assets:
          type: array
          maxItems: 12
          uniqueItems: true
          default: []
          items:
            $ref: '#/components/schemas/AvatarSourceAcquisitionSourceAsset'
        candidate_spec:
          type: object
          additionalProperties: false
          properties:
            count:
              type: integer
              minimum: 1
              maximum: 12
            framing:
              type: string
              enum:
                - portrait
                - upper_body
                - full_body
                - mixed
            aspect_ratio:
              type: string
              enum:
                - '9:16'
                - '4:5'
                - '1:1'
            min_width:
              type: integer
              minimum: 256
              maximum: 4096
            min_height:
              type: integer
              minimum: 256
              maximum: 4096
          required:
            - count
            - framing
            - aspect_ratio
            - min_width
            - min_height
        quality_tier:
          type: string
          enum:
            - draft
            - standard
            - premium
      required:
        - recipe
        - prompt
        - candidate_spec
        - quality_tier
      allOf:
        - if:
            properties:
              recipe:
                const: generated_portrait_collection
          then:
            properties:
              source_assets:
                maxItems: 0
        - if:
            properties:
              recipe:
                const: derived_look_collection
          then:
            properties:
              source_assets:
                minItems: 1
                contains:
                  type: object
                  properties:
                    role:
                      const: identity_anchor
                  required:
                    - role
                minContains: 1
                maxContains: 1
    AvatarSourceAcquisitionQuote:
      type: object
      additionalProperties: false
      description: Safe account-facing quote projection. Provider, model, adapter, request IDs, and provider cost remain service-only.
      properties:
        object:
          const: avatar_source_acquisition_quote
        id:
          type: string
          format: uuid
        source_pack_id:
          type: string
          format: uuid
        state:
          type: string
          enum:
            - quoted
            - approved
            - submitting
            - submitted
            - processing
            - importing
            - succeeded
            - failed
            - cancelled
            - expired
            - unknown_acceptance
        amount:
          type: integer
          minimum: 0
        currency:
          const: credits
        pricing_version:
          type: string
        request:
          $ref: '#/components/schemas/AvatarSourceAcquisitionQuoteRequest'
        credits_reserved:
          type: boolean
        idempotent:
          type: boolean
        expires_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - source_pack_id
        - state
        - amount
        - currency
        - pricing_version
        - expires_at
        - created_at
        - updated_at
        - links
    AvatarSourceAcquisitionApprovalRequest:
      type: object
      additionalProperties: false
      properties:
        expected_amount:
          type: integer
          minimum: 0
        expected_pricing_version:
          type: string
          minLength: 1
          maxLength: 100
      required:
        - expected_amount
        - expected_pricing_version
    AvatarStudioResumeState:
      type: object
      additionalProperties: false
      description: Provider-neutral durable state for resuming one account-owned Avatar Studio flow. Reads never invoke a paid provider and omit storage keys, adapters, vendor identifiers, and provider costs.
      properties:
        object:
          const: avatar_studio_resume_state
        source_pack:
          $ref: '#/components/schemas/AvatarSourcePack'
        candidates:
          type: array
          items:
            $ref: '#/components/schemas/AvatarSourceCandidate'
        qualification:
          oneOf:
            - $ref: '#/components/schemas/AvatarSourceQualification'
            - type: 'null'
        source_acquisition_quote:
          oneOf:
            - $ref: '#/components/schemas/AvatarSourceAcquisitionQuote'
            - type: 'null'
        profile_quote:
          oneOf:
            - $ref: '#/components/schemas/AvatarProfileProvisioningQuote'
            - type: 'null'
        profile_job:
          oneOf:
            - $ref: '#/components/schemas/AvatarProfileProvisioningJob'
            - type: 'null'
        stage:
          type: string
          enum:
            - source
            - source_acquisition
            - qualifying
            - needs_attention
            - ready
            - profile_quote
            - profile_creation
            - complete
      required:
        - object
        - source_pack
        - candidates
        - qualification
        - source_acquisition_quote
        - profile_quote
        - profile_job
        - stage
    OrderedMediaImportRetryRequestV1:
      type: object
      additionalProperties: false
      properties:
        source_url:
          type: string
          format: uri
      required:
        - source_url
    WorkflowInputEnvelope:
      type: object
      description: Strict producer-owned runtime data for one immutable executable workflow input contract.
      properties:
        contract_version:
          type: string
          const: workflow_input.v1
        profile_contract:
          type: object
          properties:
            id:
              type: string
            version:
              type: string
          required:
            - id
            - version
          additionalProperties: false
        data:
          type: object
          additionalProperties: true
      required:
        - contract_version
        - profile_contract
        - data
      additionalProperties: false
    WorkflowAssetSource:
      oneOf:
        - type: object
          properties:
            type:
              const: media_id
            id:
              type: string
              format: uuid
          required:
            - type
            - id
          additionalProperties: false
        - type: object
          properties:
            type:
              const: url
            url:
              type: string
              format: uri
          required:
            - type
            - url
          additionalProperties: false
        - type: object
          properties:
            type:
              const: article_url
            url:
              type: string
              format: uri
          required:
            - type
            - url
          additionalProperties: false
    CreativePackageAsset:
      type: object
      properties:
        id:
          type: string
        source:
          $ref: '#/components/schemas/WorkflowAssetSource'
        kind:
          type: string
          enum:
            - image
            - video
            - audio
            - article
            - card
        role:
          type: string
        title:
          type: string
        notes:
          type: string
        tags:
          type: array
          items:
            type: string
        metadata:
          type: object
          additionalProperties: true
      required:
        - id
        - source
        - tags
        - metadata
      additionalProperties: false
    CreativePackageAssetGroup:
      type: object
      properties:
        group_key:
          type: string
        label:
          type: string
        asset_ids:
          type: array
          items:
            type: string
        ordered_asset_ids:
          type: array
          items:
            type: string
        strategy:
          type: string
          enum:
            - unordered
            - preferred_order
            - strict_order
          default: unordered
      required:
        - group_key
        - asset_ids
        - ordered_asset_ids
        - strategy
      additionalProperties: false
    CreativePackageBrief:
      type: object
      properties:
        hook:
          type: string
        script:
          type: string
        cta:
          type: string
        tone:
          type: string
        target_platform:
          type: string
        audience:
          type: string
      additionalProperties: false
    CreativePackagePinnedScene:
      type: object
      properties:
        scene_index:
          type: integer
          minimum: 1
        asset_id:
          type: string
        asset_group_key:
          type: string
        label:
          type: string
        overlay_primitives:
          type: array
          items:
            type: string
        host_mode:
          type: string
        lock:
          type: string
          enum:
            - none
            - soft
            - hard
      required:
        - scene_index
      additionalProperties: false
    CreativePackageStoryboardHints:
      type: object
      properties:
        must_include_tags:
          type: array
          items:
            type: string
        preferred_group_order:
          type: array
          items:
            type: string
        pinned_scenes:
          type: array
          items:
            $ref: '#/components/schemas/CreativePackagePinnedScene'
        forbidden_crops:
          type: array
          items:
            type: string
        scene_lock_mode:
          type: string
          enum:
            - none
            - soft
            - hard
        scene_count_hint:
          type: integer
          minimum: 1
          maximum: 20
      additionalProperties: false
    CameraMotionPosition:
      type: object
      description: Normalized crop-window center. Both axes range from 0 at the top or left edge to 1 at the bottom or right edge.
      properties:
        x:
          type: number
          minimum: 0
          maximum: 1
        'y':
          type: number
          minimum: 0
          maximum: 1
      required:
        - x
        - 'y'
      additionalProperties: false
    CameraMotion:
      type: object
      description: Provider-neutral camera treatment for still images. Auto uses source and output aspect ratios to favor a crop-window pan and falls back to a restrained zoom only when no meaningful crop margin exists.
      properties:
        mode:
          type: string
          enum:
            - auto
            - pan
            - zoom_in
            - zoom_out
            - static
        direction:
          type: string
          enum:
            - auto
            - left
            - right
            - up
            - down
          default: auto
        intensity:
          type: string
          enum:
            - subtle
            - balanced
            - strong
          default: balanced
        start_position:
          $ref: '#/components/schemas/CameraMotionPosition'
        end_position:
          $ref: '#/components/schemas/CameraMotionPosition'
      required:
        - mode
      additionalProperties: false
    CreativePackageRender:
      type: object
      properties:
        aspect_ratio:
          type: string
          enum:
            - '9:16'
            - '4:5'
            - '1:1'
            - '16:9'
        duration_seconds:
          type: integer
          minimum: 15
          maximum: 180
        voice:
          $ref: '#/components/schemas/VoiceRef'
        soundtrack_style:
          type: string
        motion_style:
          type: string
        visual_style:
          type: string
        image_quality:
          type: string
          enum:
            - standard
            - ultra
        include_soundtrack:
          type: boolean
        use_voice_over:
          type: boolean
        camera_motion:
          $ref: '#/components/schemas/CameraMotion'
      additionalProperties: false
    CreativePackage:
      type: object
      properties:
        assets:
          type: array
          items:
            $ref: '#/components/schemas/CreativePackageAsset'
        asset_groups:
          type: array
          items:
            $ref: '#/components/schemas/CreativePackageAssetGroup'
        brief:
          $ref: '#/components/schemas/CreativePackageBrief'
        brand:
          type: object
          additionalProperties: true
        presenter:
          type: object
          additionalProperties: true
        storyboard_hints:
          $ref: '#/components/schemas/CreativePackageStoryboardHints'
        render:
          $ref: '#/components/schemas/CreativePackageRender'
        metadata:
          type: object
          additionalProperties: true
      required:
        - assets
        - asset_groups
        - metadata
      additionalProperties: false
    WorkflowProfileSourceInput:
      type: object
      properties:
        kind:
          const: workflow_profile
        profile_key:
          type: string
        version:
          type: string
        workflow_input:
          $ref: '#/components/schemas/WorkflowInputEnvelope'
        creative_package:
          $ref: '#/components/schemas/CreativePackage'
      required:
        - kind
        - profile_key
        - creative_package
      additionalProperties: false
    WorkflowProfileBindingSourceInput:
      type: object
      properties:
        kind:
          const: workflow_profile_binding
        binding_id:
          type: string
          format: uuid
        workflow_input:
          $ref: '#/components/schemas/WorkflowInputEnvelope'
        creative_package:
          $ref: '#/components/schemas/CreativePackage'
      required:
        - kind
        - binding_id
        - creative_package
      additionalProperties: false
    SessionInputAssetSource:
      oneOf:
        - type: object
          properties:
            type:
              const: media_id
            id:
              type: string
              format: uuid
          required:
            - type
            - id
          additionalProperties: false
        - type: object
          properties:
            type:
              const: url
            url:
              type: string
              format: uri
          required:
            - type
            - url
          additionalProperties: false
        - type: object
          properties:
            type:
              const: article_url
            url:
              type: string
              format: uri
          required:
            - type
            - url
          additionalProperties: false
    SessionInputAsset:
      type: object
      properties:
        source:
          $ref: '#/components/schemas/SessionInputAssetSource'
        kind:
          type: string
          enum:
            - image
            - video
            - audio
            - article
        role:
          type: string
          enum:
            - hero
            - support
            - reference
            - source_video
            - logo
            - music
        title:
          type: string
        notes:
          type: string
      required:
        - source
      additionalProperties: false
    SessionAssetBinding:
      type: object
      description: Explicit scene-to-media assignment. The media_id must reference an account-owned media_id input asset.
      properties:
        scene_index:
          type: integer
          minimum: 0
        media_id:
          type: string
          format: uuid
        lock:
          type: boolean
          default: false
      required:
        - scene_index
        - media_id
      additionalProperties: false
    SessionSoundtrackTrack:
      type:
        - object
        - 'null'
      properties:
        track_id:
          type: string
          description: Stable filename-derived identifier from the Shotmatic soundtrack catalog.
        url:
          type: string
          format: uri
          description: Exact URL from the Shotmatic soundtrack catalog; arbitrary remote URLs are rejected at approval.
      additionalProperties: false
    AvatarTimelineAnchor:
      oneOf:
        - type: object
          properties:
            anchor:
              type: string
              enum:
                - start
                - end
            offset_ms:
              type: integer
              minimum: 0
            duration_ms:
              type: integer
              minimum: 1
            source_trim_ms:
              type: integer
              minimum: 0
          required:
            - anchor
          additionalProperties: false
        - type: object
          properties:
            anchor:
              const: scene
            scene_id:
              type: string
              minLength: 1
              maxLength: 160
            offset_ms:
              type: integer
              minimum: 0
            duration_ms:
              type: integer
              minimum: 1
            source_trim_ms:
              type: integer
              minimum: 0
          required:
            - anchor
            - scene_id
          additionalProperties: false
    AvatarSpeech:
      oneOf:
        - type: object
          properties:
            kind:
              const: script_segments
            script_segment_ids:
              type: array
              minItems: 1
              maxItems: 100
              items:
                type: string
                pattern: ^speech_[a-f0-9]{32}$
          required:
            - kind
            - script_segment_ids
          additionalProperties: false
        - type: object
          properties:
            kind:
              const: audio_asset
            audio_media_id:
              type: string
              format: uuid
          required:
            - kind
            - audio_media_id
          additionalProperties: false
    AvatarPlacement:
      type: object
      properties:
        layout:
          type: string
          enum:
            - full_frame
            - split
            - overlay
        shape:
          type: string
          enum:
            - source
            - rounded_rect
            - circle
            - cutout
        anchor:
          type: string
          enum:
            - top_left
            - top_right
            - bottom_left
            - bottom_right
            - center
        size:
          type: string
          enum:
            - sm
            - md
            - lg
            - xl
        matting:
          type: string
          enum:
            - alpha
            - chroma
            - none
        entrance:
          type: string
          enum:
            - none
            - fade
            - slide
            - scale
        exit:
          type: string
          enum:
            - none
            - fade
            - slide
            - scale
        z_index:
          type: integer
          minimum: 0
          maximum: 100
      required:
        - layout
        - shape
      additionalProperties: false
    AvatarPresentationBeat:
      type: object
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 160
        role:
          type: string
          enum:
            - intro
            - outro
            - interstitial
            - continuous
        timeline:
          $ref: '#/components/schemas/AvatarTimelineAnchor'
        speech:
          $ref: '#/components/schemas/AvatarSpeech'
        placement:
          $ref: '#/components/schemas/AvatarPlacement'
      required:
        - id
        - role
        - timeline
        - speech
        - placement
      additionalProperties: false
    AvatarPresentationRequestV2:
      type: object
      description: Provider-neutral editable avatar presentation request compiled by Shotmatic into an internal frame-based Avatar Track.
      properties:
        contract_version:
          const: avatar_presentation_request.v2
        profile_id:
          type: string
          format: uuid
        strategy:
          type: string
          enum:
            - bookends
            - floating_host
            - split_host
            - presenter_first
            - scene_cameos
            - full_presenter
        beats:
          type: array
          minItems: 1
          maxItems: 100
          items:
            $ref: '#/components/schemas/AvatarPresentationBeat'
      required:
        - contract_version
        - profile_id
        - strategy
        - beats
      additionalProperties: false
    AvatarSessionPresentationPreset:
      type: object
      description: Concise provider-neutral placement intent resolved by Shotmatic into avatar_presentation_request.v2 before the review response.
      properties:
        strategy:
          type: string
          enum:
            - bookends
            - floating_host
            - split_host
            - presenter_first
            - scene_cameos
            - full_presenter
        scene_ids:
          type: array
          minItems: 1
          maxItems: 100
          items:
            type: string
            minLength: 1
            maxLength: 160
      required:
        - strategy
      additionalProperties: false
    SessionAvatarControls:
      type: object
      description: Provider-neutral account avatar selection plus either concise placement intent or an exact editable presentation. Removed provider and flat layout fields are rejected.
      properties:
        profile_id:
          type: string
          format: uuid
        presentation:
          $ref: '#/components/schemas/AvatarPresentationRequestV2'
        presentation_preset:
          $ref: '#/components/schemas/AvatarSessionPresentationPreset'
        performance_policy:
          type: object
          description: Provider-neutral quality and failover policy applied independently to each speaking allocation.
          properties:
            quality_tier:
              type: string
              enum:
                - draft
                - standard
                - premium
                - reuse
              default: standard
            fallback:
              type: string
              enum:
                - none
                - same_identity_same_tier
                - allow_lower_tier
              default: same_identity_same_tier
          required:
            - quality_tier
            - fallback
          additionalProperties: false
          default:
            quality_tier: standard
            fallback: same_identity_same_tier
      required:
        - profile_id
      oneOf:
        - required:
            - presentation
        - required:
            - presentation_preset
      additionalProperties: false
    SessionSelectedAsset:
      type: object
      properties:
        scene_index:
          type: integer
          minimum: 0
        asset_id:
          type: string
      required:
        - scene_index
        - asset_id
      additionalProperties: false
    SessionAdControls:
      type: object
      properties:
        product:
          type: string
        audience:
          type: string
        key_points:
          type: array
          minItems: 1
          maxItems: 20
          items:
            type: string
        cta:
          type: string
        selected_assets_by_scene:
          type: array
          maxItems: 40
          items:
            $ref: '#/components/schemas/SessionSelectedAsset'
      required:
        - product
        - key_points
      additionalProperties: false
    SessionArticleControls:
      type: object
      properties:
        kind:
          const: article_url
        url:
          type: string
          format: uri
      required:
        - kind
        - url
      additionalProperties: false
    SessionAudioControls:
      type: object
      properties:
        source_audio_media_id:
          type: string
          format: uuid
      required:
        - source_audio_media_id
      additionalProperties: false
    SessionCreativeControls:
      type: object
      description: Durable type-specific creation intent. The block matching video_type is required by the runtime schema.
      properties:
        video_type:
          type: string
          enum:
            - simple
            - article_video
            - ad_generator
            - avatar_video
            - audio_video
          default: simple
        soundtrack:
          $ref: '#/components/schemas/SessionSoundtrackTrack'
        avatar:
          $ref: '#/components/schemas/SessionAvatarControls'
        ad:
          $ref: '#/components/schemas/SessionAdControls'
        article:
          $ref: '#/components/schemas/SessionArticleControls'
        audio:
          $ref: '#/components/schemas/SessionAudioControls'
      additionalProperties: false
    SessionCaptionStyle:
      type:
        - string
        - 'null'
      enum:
        - viral
        - bold
        - minimal
        - story
        - null
    OrderedMediaAssetV1:
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 120
        kind:
          const: video
        role:
          const: source_video
        source:
          type: object
          additionalProperties: false
          properties:
            type:
              const: media_id
            id:
              type: string
              format: uuid
          required:
            - type
            - id
      required:
        - id
        - kind
        - role
        - source
    OrderedMediaTransitionV1:
      oneOf:
        - type: object
          additionalProperties: false
          properties:
            type:
              const: cut
            duration_seconds:
              const: 0
          required:
            - type
            - duration_seconds
        - type: object
          additionalProperties: false
          properties:
            type:
              const: crossfade
            duration_seconds:
              type: number
              minimum: 0.1
              maximum: 1.5
          required:
            - type
            - duration_seconds
    OrderedMediaFramingHintV1:
      type: object
      additionalProperties: false
      properties:
        focal_point:
          type: object
          additionalProperties: false
          properties:
            x:
              type: number
              minimum: 0
              maximum: 1
            'y':
              type: number
              minimum: 0
              maximum: 1
          required:
            - x
            - 'y'
        protected_regions:
          type: array
          maxItems: 8
          items:
            type: object
            additionalProperties: false
            properties:
              id:
                type: string
                minLength: 1
                maxLength: 120
              x:
                type: number
                minimum: 0
                maximum: 1
              'y':
                type: number
                minimum: 0
                maximum: 1
              width:
                type: number
                exclusiveMinimum: 0
                maximum: 1
              height:
                type: number
                exclusiveMinimum: 0
                maximum: 1
              priority:
                type: string
                enum:
                  - prefer
                  - required
            required:
              - id
              - x
              - 'y'
              - width
              - height
              - priority
    OrderedMediaOverlayV1:
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 120
        role:
          type: string
          enum:
            - scene_label
            - lower_third
            - disclosure
            - cta
            - brand_watermark
        text:
          type: string
          maxLength: 120
          description: Required except for brand_watermark.
        variant:
          type: string
          minLength: 1
          maxLength: 120
          description: Binding-owned key required only for brand_watermark.
        scope:
          oneOf:
            - type: object
              additionalProperties: false
              properties:
                type:
                  const: all_segments
              required:
                - type
            - type: object
              additionalProperties: false
              properties:
                type:
                  const: segment_ids
                segment_ids:
                  type: array
                  minItems: 1
                  items:
                    type: string
                    minLength: 1
                    maxLength: 120
              required:
                - type
                - segment_ids
      required:
        - id
        - role
    OrderedMediaSegmentV1:
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 120
        asset_id:
          type: string
          minLength: 1
          maxLength: 120
        source_range:
          type: object
          additionalProperties: false
          description: Half-open range [in_seconds, out_seconds); out must exceed in.
          properties:
            in_seconds:
              type: number
              minimum: 0
            out_seconds:
              type: number
              exclusiveMinimum: 0
          required:
            - in_seconds
            - out_seconds
        transition_out:
          $ref: '#/components/schemas/OrderedMediaTransitionV1'
        framing_hint:
          $ref: '#/components/schemas/OrderedMediaFramingHintV1'
        overlays:
          type: array
          description: Segment-local overlays must omit scope.
          items:
            $ref: '#/components/schemas/OrderedMediaOverlayV1'
          default: []
      required:
        - id
        - asset_id
        - source_range
        - overlays
    OrderedMediaCompositionV1:
      type: object
      additionalProperties: false
      properties:
        contract_version:
          const: ordered_media.v1
        audio_policy:
          const: mute
        segments:
          type: array
          minItems: 1
          maxItems: 30
          description: Order is authoritative; IDs must be unique and every asset must be referenced.
          items:
            $ref: '#/components/schemas/OrderedMediaSegmentV1'
        overlays:
          type: array
          description: Global overlays require an explicit valid scope.
          items:
            $ref: '#/components/schemas/OrderedMediaOverlayV1'
          default: []
      required:
        - contract_version
        - audio_policy
        - segments
        - overlays
    OrderedMediaEstimateV1:
      type: object
      additionalProperties: true
      properties:
        id:
          type: string
        amount:
          type: integer
          minimum: 0
        unit:
          const: credits
      required:
        - id
        - amount
        - unit
    AvatarSessionBatchAuthorizationRequest:
      type: object
      additionalProperties: false
      properties:
        id:
          type: string
          format: uuid
        expected_amount:
          type: integer
          minimum: 1
        expected_pricing_version:
          const: avatar_session_pricing.v3
      required:
        - id
        - expected_amount
        - expected_pricing_version
    AvatarSessionPerformanceLineItem:
      type: object
      additionalProperties: false
      properties:
        kind:
          type: string
          enum:
            - remotion_composition
            - avatar_performance
        label:
          type: string
        beat_ids:
          type: array
          items:
            type: string
        duration_ceiling_ms:
          type: integer
          minimum: 1
        output:
          type: object
          additionalProperties: false
          properties:
            background:
              type: string
              enum:
                - transparent
                - source
            format:
              type: string
              enum:
                - webm
                - mp4
          required:
            - background
            - format
        execution_source:
          type: string
          enum:
            - provider_performance
            - motion_base_lip_sync
            - motion_base_lip_sync_matting
        motion_base_id:
          type: string
          format: uuid
          description: Present only when execution_source uses a saved Motion Base. Provider and private media details remain service-only.
        credits:
          type: integer
          minimum: 0
        state:
          const: allocated
      required:
        - kind
        - label
        - credits
        - state
    AvatarSessionPerformanceBatch:
      type: object
      description: Safe account/session projection of one frozen creator-level Avatar Performance batch. Provider, raw USD, binding, command, and private storage details remain service-only.
      additionalProperties: false
      properties:
        object:
          const: avatar_performance_batch
        id:
          type: string
          format: uuid
        session_id:
          type: string
          format: uuid
        session_revision:
          type: integer
          minimum: 1
        state:
          type: string
          enum:
            - quoted
            - approved
            - materializing_audio
            - generating
            - ready
            - rendering
            - completed
            - additional_approval_required
            - failed
            - cancelled
            - unknown_acceptance
            - expired
            - superseded
        pricing:
          type: object
          additionalProperties: false
          properties:
            contract_version:
              const: avatar_session_pricing.v3
            pricing_version:
              const: avatar_session_pricing.v3
            credits:
              type: integer
              minimum: 1
            total_credits:
              type: integer
              minimum: 1
            currency:
              const: credits
            line_items:
              type: array
              items:
                $ref: '#/components/schemas/AvatarSessionPerformanceLineItem'
            explanation:
              type: string
            approval:
              type: object
              additionalProperties: false
              properties:
                expected_amount:
                  type: integer
                  minimum: 1
                expected_pricing_version:
                  const: avatar_session_pricing.v3
                expires_at:
                  type: string
                  format: date-time
              required:
                - expected_amount
                - expected_pricing_version
                - expires_at
          required:
            - contract_version
            - pricing_version
            - credits
            - total_credits
            - currency
            - line_items
            - explanation
            - approval
        credits_reserved:
          type: boolean
        actual_credits:
          type:
            - integer
            - 'null'
          minimum: 0
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        links:
          type: object
          additionalProperties:
            type: string
      required:
        - object
        - id
        - session_id
        - session_revision
        - state
        - pricing
        - credits_reserved
        - actual_credits
        - created_at
        - updated_at
        - links
    ApproveAvatarSessionBatchRequest:
      type: object
      additionalProperties: false
      properties:
        expected_amount:
          type: integer
          minimum: 1
        expected_pricing_version:
          const: avatar_session_pricing.v3
      required:
        - expected_amount
        - expected_pricing_version
    OrderedMediaRenderRunV1:
      type: object
      additionalProperties: true
      properties:
        object:
          const: render_run
        id:
          type: string
          format: uuid
        status:
          type: string
          enum:
            - queued
            - preparing
            - rendering
            - finalizing
            - completed
            - failed
        phase:
          type: string
          enum:
            - queued
            - preparing
            - rendering
            - finalizing
            - completed
            - failed
        session_id:
          type:
            - string
            - 'null'
          format: uuid
        video_id:
          type:
            - string
            - 'null'
          format: uuid
        output_url:
          type:
            - string
            - 'null'
          format: uri
        error_code:
          type:
            - string
            - 'null'
        error_message:
          type:
            - string
            - 'null'
        links:
          type: object
          additionalProperties:
            anyOf:
              - type: string
              - type: 'null'
        latest_event:
          anyOf:
            - type: object
              additionalProperties: true
            - type: 'null'
      required:
        - object
        - id
        - status
        - phase
        - links
        - latest_event
    WorkflowNodePort:
      type: object
      properties:
        name:
          type: string
        required:
          type: boolean
      required:
        - name
        - required
      additionalProperties: false
    WorkflowNodeType:
      type: object
      properties:
        object:
          type: string
          const: workflow_node_type
        id:
          type: string
          description: Immutable `type@version` identifier.
        type:
          type: string
        version:
          type: string
        description:
          type: string
        status:
          type: string
          enum:
            - active
        deprecated:
          type: boolean
          const: false
        required_capabilities:
          type: array
          items:
            type: string
        execution_class:
          type: string
          enum:
            - deterministic
        side_effects:
          type: string
          enum:
            - none
        provider_calls:
          type: string
          enum:
            - none
        cost_class:
          type: string
          enum:
            - free_compile
        schema_dialect:
          type: string
          const: https://json-schema.org/draft/2020-12/schema
        ports:
          type: object
          properties:
            inputs:
              type: array
              items:
                $ref: '#/components/schemas/WorkflowNodePort'
            outputs:
              type: array
              items:
                $ref: '#/components/schemas/WorkflowNodePort'
          required:
            - inputs
            - outputs
          additionalProperties: false
        schemas:
          type: object
          description: Draft 2020-12 schemas generated from the exact runtime Zod definitions.
          properties:
            inputs:
              type: object
              additionalProperties: true
            config:
              type: object
              additionalProperties: true
            outputs:
              type: object
              additionalProperties: true
          required:
            - inputs
            - config
            - outputs
          additionalProperties: false
      required:
        - object
        - id
        - type
        - version
        - description
        - status
        - deprecated
        - required_capabilities
        - execution_class
        - side_effects
        - provider_calls
        - cost_class
        - schema_dialect
        - ports
        - schemas
      additionalProperties: false
    WorkflowNodeTypeList:
      type: object
      properties:
        object:
          type: string
          const: workflow_node_type_list
        catalog_version:
          type: string
          const: workflow_node_type_catalog.v1
        compiler_contract_version:
          type: string
          const: executable_workflow_compiler.v1
        graph_contract_version:
          type: string
          const: workflow_profile_graph.v1
        schema_dialect:
          type: string
          const: https://json-schema.org/draft/2020-12/schema
        limits:
          type: object
          properties:
            max_nodes_per_profile:
              type: integer
              const: 200
          required:
            - max_nodes_per_profile
          additionalProperties: false
        data:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowNodeType'
        catalog_sha256:
          type: string
          pattern: ^sha256:[0-9a-f]{64}$
      required:
        - object
        - catalog_version
        - compiler_contract_version
        - graph_contract_version
        - schema_dialect
        - limits
        - data
        - catalog_sha256
      additionalProperties: false
    WorkflowProfileBlock:
      type: object
      properties:
        id:
          type: string
        type:
          type: string
        label:
          type: string
        config:
          type: object
          additionalProperties: true
      required:
        - type
        - config
      additionalProperties: false
    WorkflowProfile:
      type: object
      properties:
        object:
          const: workflow_profile
        id:
          type: string
          format: uuid
        visibility:
          type: string
          enum:
            - platform
            - client
        profile_key:
          type: string
        version:
          type: string
        name:
          type: string
        description:
          type:
            - string
            - 'null'
        authored_blocks:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowProfileBlock'
        runtime_input_schema:
          type: object
          additionalProperties: true
        supported_capabilities:
          type: array
          items:
            type: string
        sample_plan:
          type: object
          additionalProperties: true
        compiler_contract_version:
          type: string
        metadata:
          type: object
          additionalProperties: true
      required:
        - object
        - id
        - visibility
        - profile_key
        - version
        - name
        - authored_blocks
        - runtime_input_schema
        - supported_capabilities
        - sample_plan
        - compiler_contract_version
        - metadata
    WorkflowProfileDraftCreateRequest:
      type: object
      properties:
        name:
          type: string
        profile_key:
          type: string
        description:
          type:
            - string
            - 'null'
        source_description:
          type: string
        authored_blocks:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowProfileBlock'
        inferred_input_schema:
          type: object
          additionalProperties: true
        inferred_capabilities:
          type: array
          items:
            type: string
        sample_plan:
          type: object
          additionalProperties: true
        warnings:
          type: array
          items:
            type: string
        metadata:
          type: object
          additionalProperties: true
      required:
        - name
        - profile_key
        - source_description
        - authored_blocks
    WorkflowProfileDraft:
      type: object
      properties:
        object:
          const: workflow_profile_draft
        id:
          type: string
          format: uuid
        visibility:
          type: string
          const: client
        profile_key:
          type: string
        name:
          type: string
        description:
          type:
            - string
            - 'null'
        source_description:
          type: string
        authored_blocks:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowProfileBlock'
        inferred_input_schema:
          type: object
          additionalProperties: true
        inferred_capabilities:
          type: array
          items:
            type: string
        sample_plan:
          type: object
          additionalProperties: true
        warnings:
          type: array
          items:
            type: string
        metadata:
          type: object
          additionalProperties: true
      required:
        - object
        - id
        - visibility
        - profile_key
        - name
        - source_description
        - authored_blocks
        - inferred_input_schema
        - inferred_capabilities
        - sample_plan
        - warnings
        - metadata
    WorkflowProfileDetail:
      type: object
      properties:
        object:
          const: workflow_profile_detail
        profile_key:
          type: string
        latest_published:
          anyOf:
            - $ref: '#/components/schemas/WorkflowProfile'
            - type: 'null'
        latest_draft:
          anyOf:
            - $ref: '#/components/schemas/WorkflowProfileDraft'
            - type: 'null'
      required:
        - object
        - profile_key
        - latest_published
        - latest_draft
    WorkflowProfileDraftUpdateRequest:
      type: object
      properties:
        name:
          type: string
        description:
          type:
            - string
            - 'null'
        source_description:
          type: string
        authored_blocks:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowProfileBlock'
        inferred_input_schema:
          type: object
          additionalProperties: true
        inferred_capabilities:
          type: array
          items:
            type: string
        sample_plan:
          type: object
          additionalProperties: true
        warnings:
          type: array
          items:
            type: string
        metadata:
          type: object
          additionalProperties: true
    WorkflowProfilePreviewRequest:
      type: object
      properties:
        sample_creative_package:
          $ref: '#/components/schemas/CreativePackage'
        sample_workflow_input:
          $ref: '#/components/schemas/WorkflowInputEnvelope'
      additionalProperties: false
    WorkflowPlanScene:
      type: object
      properties:
        id:
          type: string
        index:
          type: integer
          minimum: 0
        role:
          type: string
        duration_seconds:
          type: number
          exclusiveMinimum: 0
          maximum: 600
        asset_ids:
          type: array
          items:
            type: string
        treatment:
          type: string
          enum:
            - still_motion
            - source_video
            - generated_clip
            - walkthrough_clip
            - before_after
            - multi_asset
            - graphic_card
        lock:
          type: string
          enum:
            - none
            - soft
            - hard
        narration:
          type:
            - string
            - 'null'
        overlay_text:
          type:
            - string
            - 'null'
        motion:
          type: object
          properties:
            mode:
              type: string
              enum:
                - auto
                - pan
                - zoom_in
                - zoom_out
                - static
            intensity:
              type: string
              enum:
                - subtle
                - balanced
                - strong
          required:
            - mode
            - intensity
          additionalProperties: false
        transition_out:
          type:
            - object
            - 'null'
          properties:
            family:
              type: string
              enum:
                - cut
                - crossfade
                - slide
                - wipe
                - chapter
            duration_seconds:
              type: number
              minimum: 0
              maximum: 10
          required:
            - family
            - duration_seconds
          additionalProperties: false
      required:
        - id
        - index
        - role
        - duration_seconds
        - asset_ids
        - treatment
        - lock
        - narration
        - overlay_text
        - motion
        - transition_out
      additionalProperties: false
    WorkflowPlanTrack:
      type: object
      properties:
        id:
          type: string
        kind:
          type: string
          enum:
            - overlay
            - caption
            - audio
            - presenter
        entries:
          type: array
          items:
            type: object
            additionalProperties: true
      required:
        - id
        - kind
        - entries
      additionalProperties: false
    WorkflowPlanMaterialUnit:
      type: object
      properties:
        id:
          type: string
        class:
          type: string
        target_id:
          type: string
        execution:
          type: string
          enum:
            - deterministic
            - uploaded
            - provider_generated
        dependency_ids:
          type: array
          items:
            type: string
        required_capability:
          type:
            - string
            - 'null'
        input_sha256:
          type: string
          pattern: ^sha256:[0-9a-f]{64}$
        reuse_scope:
          type: string
          enum:
            - request
            - account
            - project
            - none
        estimate:
          type: object
          properties:
            unit:
              type: string
            quantity:
              type: number
              minimum: 0
          required:
            - unit
            - quantity
          additionalProperties: false
      required:
        - id
        - class
        - target_id
        - execution
        - dependency_ids
        - required_capability
        - input_sha256
        - reuse_scope
        - estimate
      additionalProperties: false
    WorkflowPlanDelivery:
      type: object
      properties:
        id:
          type: string
        aspect_ratio:
          type: string
          enum:
            - '9:16'
            - '4:5'
            - '1:1'
            - '16:9'
        width:
          type: integer
          minimum: 1
        height:
          type: integer
          minimum: 1
        fps:
          type: integer
          minimum: 1
          maximum: 120
        duration_seconds:
          type: number
          exclusiveMinimum: 0
          maximum: 3600
      required:
        - id
        - aspect_ratio
        - width
        - height
        - fps
        - duration_seconds
      additionalProperties: false
    WorkflowPlan:
      type: object
      description: Deterministic provider-neutral intermediate representation used for review, quote, approval, and rendering.
      properties:
        contract_version:
          type: string
          const: workflow_plan.v1
        workflow:
          type: object
          properties:
            profile_key:
              type: string
            profile_version:
              type: string
            binding_id:
              type:
                - string
                - 'null'
            binding_version:
              type:
                - string
                - 'null'
            compiler_version:
              type: string
              const: executable_workflow_compiler.v1
          required:
            - profile_key
            - profile_version
            - binding_id
            - binding_version
            - compiler_version
          additionalProperties: false
        source:
          type: object
          properties:
            input_contract_id:
              type: string
            input_contract_version:
              type: string
            input_sha256:
              type: string
              pattern: ^sha256:[0-9a-f]{64}$
            creative_package_sha256:
              type:
                - string
                - 'null'
              pattern: ^sha256:[0-9a-f]{64}$
            capability_snapshot_sha256:
              type: string
              pattern: ^sha256:[0-9a-f]{64}$
          required:
            - input_contract_id
            - input_contract_version
            - input_sha256
            - creative_package_sha256
            - capability_snapshot_sha256
          additionalProperties: false
        scenes:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowPlanScene'
        tracks:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowPlanTrack'
        materials:
          type: object
          properties:
            units:
              type: array
              items:
                $ref: '#/components/schemas/WorkflowPlanMaterialUnit'
          required:
            - units
          additionalProperties: false
        deliveries:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowPlanDelivery'
        decisions:
          type: array
          items:
            type: object
            additionalProperties: true
        fallbacks:
          type: array
          items:
            type: object
            additionalProperties: true
        warnings:
          type: array
          items:
            type: object
            additionalProperties: true
        estimate_inputs:
          type: array
          items:
            type: object
            properties:
              unit:
                type: string
              quantity:
                type: number
                minimum: 0
            required:
              - unit
              - quantity
            additionalProperties: false
        hashes:
          type: object
          properties:
            graph_sha256:
              type: string
              pattern: ^sha256:[0-9a-f]{64}$
            scenes_sha256:
              type: string
              pattern: ^sha256:[0-9a-f]{64}$
            tracks_sha256:
              type: string
              pattern: ^sha256:[0-9a-f]{64}$
            materials_sha256:
              type: string
              pattern: ^sha256:[0-9a-f]{64}$
            deliveries_sha256:
              type: string
              pattern: ^sha256:[0-9a-f]{64}$
            plan_sha256:
              type: string
              pattern: ^sha256:[0-9a-f]{64}$
          required:
            - graph_sha256
            - scenes_sha256
            - tracks_sha256
            - materials_sha256
            - deliveries_sha256
            - plan_sha256
          additionalProperties: false
      required:
        - contract_version
        - workflow
        - source
        - scenes
        - tracks
        - materials
        - deliveries
        - decisions
        - fallbacks
        - warnings
        - estimate_inputs
        - hashes
      additionalProperties: false
    WorkflowProfilePreview:
      type: object
      properties:
        object:
          const: workflow_profile_preview
        profile_key:
          type: string
        using:
          type: string
          enum:
            - draft
            - published
        authored_blocks:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowProfileBlock'
        supported_capabilities:
          type: array
          items:
            type: string
        sample_plan:
          type: object
          additionalProperties: true
        workflow_plan:
          $ref: '#/components/schemas/WorkflowPlan'
        preview:
          type: object
          properties:
            scene_count:
              type: integer
            sample_asset_count:
              type: integer
            labels_enabled:
              type: boolean
            host_enabled:
              type: boolean
            overlay_enabled:
              type: boolean
          required:
            - scene_count
            - sample_asset_count
            - labels_enabled
            - host_enabled
            - overlay_enabled
      required:
        - object
        - profile_key
        - using
        - authored_blocks
        - supported_capabilities
        - sample_plan
        - preview
    WorkflowProfilePublishRequest:
      type: object
      properties:
        draft_id:
          type: string
          format: uuid
        version:
          type: string
    WorkflowProfileBinding:
      type: object
      properties:
        object:
          const: workflow_profile_binding
        id:
          type: string
          format: uuid
        visibility:
          type: string
          const: client
        name:
          type: string
        profile_key:
          type: string
        profile_version:
          type: string
        scope_level:
          type: string
          enum:
            - organization
            - team
            - user
        scope_id:
          type:
            - string
            - 'null'
        external_id:
          type:
            - string
            - 'null'
        brand_kit_id:
          type:
            - string
            - 'null'
          format: uuid
        presenter_profile_id:
          type:
            - string
            - 'null'
          format: uuid
        defaults:
          type: object
          additionalProperties: true
        overrides:
          type: object
          additionalProperties: true
        metadata:
          type: object
          additionalProperties: true
      required:
        - object
        - id
        - visibility
        - name
        - profile_key
        - profile_version
        - scope_level
        - defaults
        - overrides
        - metadata
    WorkflowProfileBindingRequest:
      type: object
      properties:
        name:
          type: string
        profile_key:
          type: string
        profile_version:
          type: string
        scope_level:
          type: string
          enum:
            - organization
            - team
            - user
        scope_id:
          type:
            - string
            - 'null'
        external_id:
          type:
            - string
            - 'null'
        brand_kit_id:
          type:
            - string
            - 'null'
          format: uuid
        presenter_profile_id:
          type:
            - string
            - 'null'
          format: uuid
        defaults:
          type: object
          additionalProperties: true
        overrides:
          type: object
          additionalProperties: true
        metadata:
          type: object
          additionalProperties: true
      required:
        - name
        - profile_key
        - profile_version
        - scope_level
    WorkflowProfileBindingUpdateRequest:
      type: object
      properties:
        name:
          type: string
        profile_key:
          type: string
        profile_version:
          type: string
        scope_level:
          type: string
          enum:
            - organization
            - team
            - user
        scope_id:
          type:
            - string
            - 'null'
        external_id:
          type:
            - string
            - 'null'
        brand_kit_id:
          type:
            - string
            - 'null'
          format: uuid
        presenter_profile_id:
          type:
            - string
            - 'null'
          format: uuid
        defaults:
          type: object
          additionalProperties: true
        overrides:
          type: object
          additionalProperties: true
        metadata:
          type: object
          additionalProperties: true
      minProperties: 1
      additionalProperties: false
x-shotmatic-versioned-contracts:
  ordered_media.v1:
    availability: disabled_by_default
    sandbox_server: https://shotvibes-m6c94ci8t-sofabrain.vercel.app/v1
    compiler_versions:
      - ordered_media.v1
    request_schema: '#/components/schemas/OrderedMediaSessionRequestV1'
    fixture: docs/fixtures/ordered-media/v1/neutral-product-demo.request.json
