openapi: 3.1.0
info:
  title: CreatorNode Postproduction API - Compose OTIO
  description: >
    Compile a caller-defined editorial plan into a deterministic OTIO timeline
    artifact.


    Use this endpoint after planning and timestamping are already settled and
    you need a validated, deterministic handoff to downstream editing tools.
  version: 1.0.0
  contact:
    name: CreatorNode Support
    url: https://creatornode.io/support
  license:
    name: Proprietary
    url: https://creatornode.io/legal
servers:
  - url: https://api.creatornode.io/postproduction
    description: Production
tags:
  - name: Postproduction
    description: Postproduction processing endpoints
paths:
  /v1/compose-otio:
    post:
      operationId: composeOtio
      tags:
        - Postproduction
      summary: Compile a caller-defined edit plan into a deterministic OTIO timeline
      description: Accept project settings, a media manifest, an ordered video
        sequence, optional audio layers, and export hints. The endpoint
        validates references and trim ranges, preserves the caller-defined cut,
        and emits a deterministic OTIO timeline for editorial handoff.
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ComposeOtioRequest"
            examples:
              youtubeStoryboardCut:
                summary: Multi-asset explicit timeline for a short editorial cut
                value:
                  project:
                    name: Forest Morning Cut
                    fps: 24
                    resolution:
                      w: 1920
                      h: 1080
                    audioSampleRate: 48000
                  folder:
                    mediaRootPath: C:/media/projects/forest-morning
                    items:
                      - id: img-opening
                        kind: image
                        path: storyboards/forest-opening.png
                        meta:
                          w: 1920
                          h: 1080
                      - id: vid-stream
                        kind: video
                        path: rushes/stream-walk.mp4
                        meta:
                          durationSec: 8
                          fps: 24
                          w: 1920
                          h: 1080
                          hasAudio: true
                      - id: aud-music
                        kind: audio
                        path: audio/forest-piano.wav
                        meta:
                          durationSec: 6
                          sampleRate: 48000
                          channels: 2
                  intent:
                    kind: explicit
                    videoSequence:
                      - itemId: img-opening
                        fromSec: 0
                        toSec: 2
                        clipNameHint: Opening Still
                        transitionOut:
                          type: crossDissolve
                          durSec: 0.5
                      - itemId: vid-stream
                        fromSec: 1
                        toSec: 5
                        clipNameHint: Forest Walk
                        embeddedAudio:
                          enabled: true
                          role: embeddedVideo
                    audioLayers:
                      - itemId: aud-music
                        role: music
                        fromSec: 0
                        toSec: 5
                        placement: trimToFit
                        clipNameHint: Music Bed
                  rules:
                    audio:
                      duckMusicUnderVoice: true
                      duckDb: -9
                  output:
                    includeClipNames: true
                    otioSchema: Timeline.1
              resolveJsonWrapper:
                summary: Resolve-target export using the JSON success envelope
                value:
                  project:
                    name: YouTube AI Creator Storyboard Cut
                    fps: 24
                    resolution:
                      w: 1920
                      h: 1080
                    audioSampleRate: 44100
                  folder:
                    mediaRootPath: D:/editorial/storyboard-cut
                    items:
                      - id: scene-1
                        kind: image
                        path: poc/youtube-ai-creator/scene-1.jpg
                        meta:
                          w: 1920
                          h: 1080
                      - id: scene-2
                        kind: image
                        path: poc/youtube-ai-creator/scene-2.jpg
                        meta:
                          w: 1920
                          h: 1080
                      - id: narration-track
                        kind: audio
                        path: poc/youtube-ai-creator/narration.mp3
                        meta:
                          durationSec: 63
                          sampleRate: 44100
                          channels: 2
                          languageCode: en
                  intent:
                    kind: explicit
                    videoSequence:
                      - itemId: scene-1
                        fromSec: 0
                        toSec: 14.5
                        clipNameHint: Scene 1
                        transitionOut:
                          type: crossDissolve
                          durSec: 0.4
                      - itemId: scene-2
                        fromSec: 0
                        toSec: 16.7
                        clipNameHint: Scene 2
                    audioLayers:
                      - itemId: narration-track
                        role: voice
                        fromSec: 0
                        toSec: 62.7
                        placement: trimToFit
                        clipNameHint: Narration
                  responseFormat: json
                  output:
                    includeClipNames: true
                    includeMarkers: true
                    targetConsumer:
                      family: resolve
                    otioSchema: Timeline.1
      responses:
        "200":
          description: OTIO export compiled successfully. Default behavior returns the
            OTIO artifact directly in the response body. When
            `responseFormat=json` is requested, the artifact is wrapped in the
            standard JSON success envelope.
          headers:
            X-Request-Id:
              description: Unique request identifier.
              schema:
                type: string
                example: req_otio_123
            X-Tier:
              description: Resolved APIM tier for the request.
              schema:
                type: string
                example: premium
            X-OTIO-Schema:
              description: OTIO schema version used for the export.
              schema:
                type: string
                example: Timeline.1
            X-Composition-Mode:
              description: Compose mode used to build the artifact.
              schema:
                type: string
                example: explicit
            X-Processing-Time-Ms:
              description: End-to-end processing time in milliseconds.
              schema:
                type: integer
                example: 63
            Content-Disposition:
              description: Present for default file-mode responses.
              schema:
                type: string
                example: attachment; filename="Forest Morning Cut.otio"
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/ComposeOtioArtifact"
                  - $ref: "#/components/schemas/ComposeOtioJsonSuccessResponse"
              examples:
                fileModeArtifact:
                  summary: Default `responseFormat=file` artifact body
                  value:
                    OTIO_SCHEMA: Timeline.1
                    name: Forest Morning Cut
                    metadata:
                      creatornode:
                        projectName: Forest Morning Cut
                        mode: explicit
                        fps: 24
                        targetConsumer:
                          family: generic
                    tracks:
                      OTIO_SCHEMA: Stack.1
                      children:
                        - OTIO_SCHEMA: Track.1
                          name: V1
                          kind: Video
                          children:
                            - OTIO_SCHEMA: Clip.1
                              name: Opening Still
                            - OTIO_SCHEMA: Transition.1
                            - OTIO_SCHEMA: Clip.1
                              name: Forest Walk
                        - OTIO_SCHEMA: Track.1
                          name: A1
                          kind: Audio
                          children:
                            - OTIO_SCHEMA: Clip.1
                              name: Music Bed
                jsonWrapper:
                  summary: Explicit `responseFormat=json` success envelope
                  value:
                    success: true
                    data:
                      OTIO_SCHEMA: Timeline.1
                      name: YouTube AI Creator Storyboard Cut
                      metadata:
                        resolve:
                          targetConsumerFamily: resolve
                          importStrategy: compatibility
                      tracks:
                        OTIO_SCHEMA: Stack.1
                        children:
                          - OTIO_SCHEMA: Track.1
                            name: Video 1
                            kind: Video
                            children:
                              - OTIO_SCHEMA: Clip.1
                                name: Scene 1
                              - OTIO_SCHEMA: Transition.1
                              - OTIO_SCHEMA: Clip.1
                                name: Scene 2
                          - OTIO_SCHEMA: Track.1
                            name: Audio 2
                            kind: Audio
                            children:
                              - OTIO_SCHEMA: Clip.1
                                name: Narration
                    meta:
                      requestId: req_otio_123
                      processingTimeMs: 63
                      mode: explicit
                      format: otio
                      filename: YouTube AI Creator Storyboard Cut.otio
                      contentType: application/json; charset=utf-8
                      otioSchema: Timeline.1
                    recommendations:
                      - type: tip
                        title: Use stable asset metadata
                        message: Stable item ids, complete metadata, and consistent project FPS make
                          OTIO exports more predictable across downstream tools.
                        priority: low
        "400":
          description: Request validation failed. Common codes include `VALIDATION_ERROR`,
            `TOO_MANY_FILES`, `UNSUPPORTED_MEDIA_KIND`,
            `MISSING_REQUIRED_METADATA`, and `VIDEO_FPS_MISMATCH`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                tooManyFiles:
                  summary: Manifest item count exceeds current plan limits
                  value:
                    success: false
                    error:
                      code: TOO_MANY_FILES
                      message: Too many manifest items for the current plan
                      details:
                        folderItemCount: 21
                        maxFolderItems: 20
                    meta:
                      requestId: req_otio_123
                validationError:
                  summary: Request payload shape is invalid
                  value:
                    success: false
                    error:
                      code: VALIDATION_ERROR
                      message: Request validation failed
                    meta:
                      requestId: req_otio_123
        "401":
          description: Unauthorized - invalid or missing API key
        "413":
          description: JSON success envelope would exceed the allowed artifact payload size
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                success: false
                error:
                  code: ARTIFACT_TOO_LARGE
                  message: JSON artifact payload exceeded the maximum allowed size
                meta:
                  requestId: req_otio_123
        "422":
          description: Explicit timeline request could not be composed into a valid timeline
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                success: false
                error:
                  code: CANNOT_COMPOSE
                  message: Timeline inputs could not be composed into a valid edit
                  details:
                    reason: clipExceedsSource
                meta:
                  requestId: req_otio_123
                recommendations:
                  - type: tip
                    title: Re-check clip timing
                    message: Ensure clip ranges and audio placements fit inside the source durations
                      you provided.
                    priority: high
        "429":
          description: Rate limited - too many requests
        "500":
          description: Deterministic OTIO compilation failed after plan validation
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                success: false
                error:
                  code: OTIO_COMPILE_FAILED
                  message: Deterministic compilation to OTIO failed
                meta:
                  requestId: req_otio_123
        "504":
          description: End-to-end processing exceeded the public processing budget
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                success: false
                error:
                  code: PROCESSING_TIMEOUT
                  message: Processing exceeded time limit
                meta:
                  requestId: req_otio_123
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: APIM subscription key for authenticated access. Without a key,
        requests use free tier limits.
  schemas:
    Recommendation:
      type: object
      required:
        - type
        - title
        - message
      properties:
        type:
          type: string
          enum:
            - upgrade
            - top_up
            - feature
            - tip
            - warning
            - fix
        title:
          type: string
        message:
          type: string
        action:
          type: object
          required:
            - label
            - url
          properties:
            label:
              type: string
            url:
              type: string
              format: uri
        priority:
          type: string
          enum:
            - low
            - medium
            - high
          default: low
    ErrorResponse:
      type: object
      required:
        - success
        - error
      properties:
        success:
          type: boolean
          enum:
            - false
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Endpoint-specific error code, for example VALIDATION_ERROR,
                UPLOAD_SIZE_EXCEEDED, AUDIO_PROBE_FAILED, CANNOT_ALIGN, or
                PROCESSING_TIMEOUT.
            message:
              type: string
            details:
              type: object
              additionalProperties: true
        meta:
          type: object
          properties:
            requestId:
              type: string
        recommendations:
          type: array
          items:
            $ref: "#/components/schemas/Recommendation"
    ComposeOtioResponseFormat:
      type: string
      enum:
        - file
        - json
      default: file
      description: Response mode. `file` returns the OTIO artifact directly as the
        response body with attachment headers. `json` wraps the same OTIO
        artifact in the standard success envelope.
    ComposeOtioTargetConsumer:
      oneOf:
        - type: object
          properties:
            family:
              type: string
              enum:
                - generic
                - resolve
            profile:
              type: string
              minLength: 1
          additionalProperties: false
        - type: object
          required:
            - family
            - profile
          properties:
            family:
              type: string
              enum:
                - creatornode
            profile:
              type: string
              minLength: 1
              description: Consumer-specific profile identifier.
          additionalProperties: false
      description: Optional export target hint for downstream editorial consumers.
    ComposeOtioTransitionParams:
      type: object
      properties:
        direction:
          type: string
          enum:
            - leftToRight
            - rightToLeft
      additionalProperties: false
    ComposeOtioTransition:
      type: object
      required:
        - type
        - durSec
      properties:
        type:
          type: string
          description: Shared transition intent. Only `crossDissolve` is supported in v1.
          enum:
            - crossDissolve
        durSec:
          type: number
          exclusiveMinimum: 0
        params:
          $ref: "#/components/schemas/ComposeOtioTransitionParams"
      additionalProperties: false
    ComposeOtioEmbeddedAudio:
      type: object
      properties:
        enabled:
          type: boolean
        separateTrack:
          type: boolean
          description: When true, emit the video clip's source audio as a separate
            audio-track clip instead of embedded clip audio on the video track.
        trackId:
          type: string
          minLength: 1
          description: Optional audio track group id used only when separateTrack=true.
        role:
          type: string
          enum:
            - voice
            - music
            - sfx
            - embeddedVideo
      additionalProperties: false
    ComposeOtioVideoSequenceItem:
      type: object
      required:
        - itemId
        - fromSec
        - toSec
      properties:
        itemId:
          type: string
          minLength: 1
        fromSec:
          type: number
          minimum: 0
        toSec:
          type: number
          exclusiveMinimum: 0
        transitionOut:
          $ref: "#/components/schemas/ComposeOtioTransition"
        clipNameHint:
          type: string
          minLength: 1
        embeddedAudio:
          $ref: "#/components/schemas/ComposeOtioEmbeddedAudio"
      additionalProperties: false
    ComposeOtioAudioLayerItem:
      type: object
      required:
        - itemId
        - fromSec
      properties:
        itemId:
          type: string
          minLength: 1
        role:
          type: string
          enum:
            - voice
            - music
            - sfx
            - embeddedVideo
        trackId:
          type: string
          minLength: 1
          description: Layers sharing the same trackId are merged onto a single audio
            track, ordered by fromSec.
        fromSec:
          type: number
          minimum: 0
        toSec:
          type: number
          exclusiveMinimum: 0
        placement:
          type: string
          enum:
            - trimToFit
            - loopToFit
            - full
          description: Use loopToFit to repeat an audio source until it fills the
            requested interval; the final repetition is trimmed if only part of
            the loop fits.
        clipNameHint:
          type: string
          minLength: 1
      additionalProperties: false
    ComposeOtioImageAsset:
      type: object
      required:
        - id
        - kind
        - path
        - meta
      properties:
        id:
          type: string
          minLength: 1
        kind:
          type: string
          enum:
            - image
        path:
          type: string
          minLength: 1
          description: Relative manifest path for this asset, resolved from
            folder.mediaRootPath when a local import root is provided.
          example: storyboards/forest-opening.png
        meta:
          type: object
          required:
            - w
            - h
          properties:
            name:
              type: string
            sha256:
              type: string
            w:
              type: integer
              minimum: 1
            h:
              type: integer
              minimum: 1
            orientation:
              type: integer
              enum:
                - 0
                - 90
                - 180
                - 270
            hasAlpha:
              type: boolean
          additionalProperties: false
      additionalProperties: false
    ComposeOtioAudioAsset:
      type: object
      required:
        - id
        - kind
        - path
        - meta
      properties:
        id:
          type: string
          minLength: 1
        kind:
          type: string
          enum:
            - audio
        path:
          type: string
          minLength: 1
          description: Relative manifest path for this asset, resolved from
            folder.mediaRootPath when a local import root is provided.
          example: audio/forest-piano.wav
        meta:
          type: object
          required:
            - durationSec
          properties:
            name:
              type: string
            sha256:
              type: string
            durationSec:
              type: number
              exclusiveMinimum: 0
            sampleRate:
              type: integer
              minimum: 1
            channels:
              type: integer
              enum:
                - 1
                - 2
            languageCode:
              type: string
          additionalProperties: false
      additionalProperties: false
    ComposeOtioVideoAsset:
      type: object
      required:
        - id
        - kind
        - path
        - meta
      properties:
        id:
          type: string
          minLength: 1
        kind:
          type: string
          enum:
            - video
        path:
          type: string
          minLength: 1
          description: Relative manifest path for this asset, resolved from
            folder.mediaRootPath when a local import root is provided.
          example: rushes/stream-walk.mp4
        meta:
          type: object
          required:
            - durationSec
            - fps
          properties:
            name:
              type: string
            sha256:
              type: string
            durationSec:
              type: number
              exclusiveMinimum: 0
            fps:
              type: integer
              enum:
                - 24
                - 25
                - 30
                - 50
                - 60
            w:
              type: integer
              minimum: 1
            h:
              type: integer
              minimum: 1
            hasAudio:
              type: boolean
          additionalProperties: false
      additionalProperties: false
    ComposeOtioAsset:
      oneOf:
        - $ref: "#/components/schemas/ComposeOtioImageAsset"
        - $ref: "#/components/schemas/ComposeOtioAudioAsset"
        - $ref: "#/components/schemas/ComposeOtioVideoAsset"
    ComposeOtioProject:
      type: object
      required:
        - name
        - fps
        - resolution
      properties:
        name:
          type: string
          minLength: 1
        fps:
          type: integer
          enum:
            - 24
            - 25
            - 30
            - 50
            - 60
        resolution:
          type: object
          required:
            - w
            - h
          properties:
            w:
              type: integer
              minimum: 1
            h:
              type: integer
              minimum: 1
          additionalProperties: false
        startTimecode:
          type: string
        audioSampleRate:
          type: integer
          enum:
            - 44100
            - 48000
      additionalProperties: false
    ComposeOtioFolder:
      type: object
      required:
        - items
      properties:
        rootId:
          type: string
        rootHint:
          type: string
        mediaRootPath:
          type: string
          description: Optional absolute local filesystem root used to derive importable
            file URLs for OTIO consumers such as Resolve. When omitted, asset
            `path` values remain relative manifest identifiers and `target_url`
            values are emitted as relative references.
          example: C:/media/projects/example-project
        items:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/ComposeOtioAsset"
      additionalProperties: false
    ComposeOtioIntent:
      type: object
      required:
        - kind
        - videoSequence
      properties:
        kind:
          type: string
          enum:
            - explicit
        videoSequence:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/ComposeOtioVideoSequenceItem"
        audioLayers:
          type: array
          items:
            $ref: "#/components/schemas/ComposeOtioAudioLayerItem"
      additionalProperties: false
    ComposeOtioRules:
      type: object
      properties:
        conform:
          type: string
          enum:
            - fill
            - fit
        maxTransitionSec:
          type: number
          exclusiveMinimum: 0
        defaultTransition:
          $ref: "#/components/schemas/ComposeOtioTransition"
        audio:
          type: object
          properties:
            duckMusicUnderVoice:
              type: boolean
            duckDb:
              type: number
          additionalProperties: false
      additionalProperties: false
    ComposeOtioOutput:
      type: object
      properties:
        includeMarkers:
          type: boolean
        includeClipNames:
          type: boolean
        targetConsumer:
          $ref: "#/components/schemas/ComposeOtioTargetConsumer"
        otioSchema:
          type: string
          enum:
            - Timeline.1
      additionalProperties: false
    ComposeOtioRequest:
      type: object
      required:
        - project
        - folder
        - intent
      properties:
        project:
          $ref: "#/components/schemas/ComposeOtioProject"
        folder:
          $ref: "#/components/schemas/ComposeOtioFolder"
        intent:
          $ref: "#/components/schemas/ComposeOtioIntent"
        rules:
          $ref: "#/components/schemas/ComposeOtioRules"
        responseFormat:
          $ref: "#/components/schemas/ComposeOtioResponseFormat"
        output:
          $ref: "#/components/schemas/ComposeOtioOutput"
      additionalProperties: false
      example:
        project:
          name: Forest Morning Cut
          fps: 24
          resolution:
            w: 1920
            h: 1080
          audioSampleRate: 48000
        folder:
          mediaRootPath: C:/media/projects/forest-morning
          items:
            - id: img-opening
              kind: image
              path: storyboards/forest-opening.png
              meta:
                w: 1920
                h: 1080
            - id: vid-stream
              kind: video
              path: rushes/stream-walk.mp4
              meta:
                durationSec: 8
                fps: 24
                w: 1920
                h: 1080
                hasAudio: true
            - id: aud-music
              kind: audio
              path: audio/forest-piano.wav
              meta:
                durationSec: 6
                sampleRate: 48000
                channels: 2
        intent:
          kind: explicit
          videoSequence:
            - itemId: img-opening
              fromSec: 0
              toSec: 2
              clipNameHint: Opening Still
              transitionOut:
                type: crossDissolve
                durSec: 0.5
            - itemId: vid-stream
              fromSec: 1
              toSec: 5
              clipNameHint: Forest Walk
              embeddedAudio:
                enabled: true
                role: embeddedVideo
          audioLayers:
            - itemId: aud-music
              role: music
              fromSec: 0
              toSec: 5
              placement: trimToFit
              clipNameHint: Music Bed
        rules:
          audio:
            duckMusicUnderVoice: true
            duckDb: -9
        output:
          includeClipNames: true
          otioSchema: Timeline.1
    ComposeOtioArtifactTrackChild:
      type: object
      required:
        - OTIO_SCHEMA
      properties:
        OTIO_SCHEMA:
          type: string
        name:
          type: string
      additionalProperties: true
    ComposeOtioArtifactTrack:
      type: object
      required:
        - OTIO_SCHEMA
        - name
        - kind
        - children
      properties:
        OTIO_SCHEMA:
          type: string
          example: Track.1
        name:
          type: string
          example: Video 1
        kind:
          type: string
          example: Video
        children:
          type: array
          items:
            $ref: "#/components/schemas/ComposeOtioArtifactTrackChild"
      additionalProperties: true
    ComposeOtioArtifact:
      type: object
      required:
        - OTIO_SCHEMA
        - name
        - tracks
      properties:
        OTIO_SCHEMA:
          type: string
          example: Timeline.1
        name:
          type: string
          example: Forest Morning Cut
        metadata:
          type: object
          additionalProperties: true
        tracks:
          type: object
          required:
            - OTIO_SCHEMA
            - children
          properties:
            OTIO_SCHEMA:
              type: string
              example: Stack.1
            children:
              type: array
              items:
                $ref: "#/components/schemas/ComposeOtioArtifactTrack"
          additionalProperties: true
    ComposeOtioJsonSuccessResponse:
      type: object
      required:
        - success
        - data
        - meta
      properties:
        success:
          type: boolean
          enum:
            - true
        data:
          $ref: "#/components/schemas/ComposeOtioArtifact"
        meta:
          type: object
          required:
            - requestId
            - processingTimeMs
            - mode
            - format
            - filename
            - contentType
            - otioSchema
          properties:
            requestId:
              type: string
              example: req_otio_123
            processingTimeMs:
              type: integer
              minimum: 0
              example: 63
            mode:
              type: string
              enum:
                - explicit
            format:
              type: string
              enum:
                - otio
            filename:
              type: string
              example: Forest Morning Cut.otio
            contentType:
              type: string
              enum:
                - application/json; charset=utf-8
            otioSchema:
              type: string
              enum:
                - Timeline.1
        recommendations:
          type: array
          items:
            $ref: "#/components/schemas/Recommendation"
