---
title: "Validate a composition body without saving it (dry-run)"
method: POST
path: "/v1/composition/validate"
tags: ["compositions"]
---

# Validate a composition body without saving it (dry-run)

`POST /v1/composition/validate`

## Request

- Base URL: `https://api.cueframe.ai`
- URL: `https://api.cueframe.ai/v1/composition/validate`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://api.cueframe.ai/api/auth/mcp/authorize`, token URL `https://api.cueframe.ai/api/auth/mcp/token`)
  - API key in header `PAYMENT-SIGNATURE`

Dry-run the FULL save-time authoring guard set against a Composition body WITHOUT persisting it and without a project. Runs the same pure, DB-free guards saveComposition enforces — wire shape, primitive membership (unknown_primitive / custom_missing_sourcecode), required-param shape (invalid_primitive_params), clip↔track family (clip_source_track_mismatch), reframe coverage (reframe_coverage_gap), and font admission (font_not_reproducible: a caption/param face that is neither bundled nor sealed in a brand font asset) — and returns ONE shape, `200 { valid, errors[] }`, for BOTH wire-shape and deep-invariant failures (no 400-vs-200 split). `valid:false` lists every authoring error (sanitized envelopes) to fix before save/render. The ONLY checks NOT run here are the two DB-bound ones — trim-bounds (media duration) and image/asset-on-video-track (media kind) — which can only be caught at save/render.

## Request body

- Composition
  - `v` 1, required
  - `format` Format, required
    - `aspectRatio` '16:9' | '9:16' | '1:1' | '4:5', required
    - `fps` union
      - 24
      - 25
      - 30
      - 60
    - `resolution` 'hd' | 'fhd' | '4k'
    - `platform` string
  - `tracks` object[], required
    - `id` string, required
    - `kind` 'video' | 'audio' | 'image' | 'overlay' | 'effect', required
    - `name` string
    - `enabled` boolean
    - `volume` number
    - `contents` union[], required
      - union
        - TimelineClip
          - `id` string, required
          - `startTime` number, required
          - `duration` number, required
          - `source` union, required
            - object
              - …
            - object
              - …
            - object
              - …
            - object
              - …
            - object
              - …
            - object
              - …
          - `kind` 'video' | 'audio' | 'image' | 'overlay' | 'effect'
          - `opacity` number
          - `scale` number
          - `rotation` number
          - `position` object: Clip CENTRE as a fraction of the frame: {x:0.5,y:0.5} is dead centre, 0 to 1 is inside the frame, and a little overshoot (down to -1, up to 2) is how an entrance starts off-canvas. NOT pixels: the renderer maps x to `left: x*100%`, so `x: 180` sits 180 frame-widths away and draws nothing.
            - `x` number, required
            - `y` number, required
          - `region` object
            - `x` number, required
            - `y` number, required
            - `w` number, required
            - `h` number, required
          - `fit` 'cover' | 'contain'
          - `captionVisibility` 'visible' | 'hidden': Video media clip only. Hide native captions while this clip is topmost and covers the lower picture, or when an opaque, effect-free clip has an explicitly full-frame region, even with fit:contain. Caption words and timings remain stored and resume when another visible clip takes over.
          - `effects` object[]
            - `id` string
            - `component` union
              - …
            - `window` object
              - …
            - `primitiveId` string
            - `params` object
          - `transitionIn` object
            - `type` string, required
            - `duration` number, required
            - `id` string
            - `component` union
              - …
            - `fromClipId` string
            - `toClipId` string
            - `handlePolicy` 'outgoing-overrun-at-cut-v1'
          - `linkGroupId` string
          - `tweens` object[]
            - `id` string, required
            - `propertyGroup` 'visual' | 'scale' | 'rotation' | 'position', required
            - `position` number, required
            - `duration` number, required
            - `keyframes` object, required
              - …
          - `automation` object
            - `version` 1, required
            - `lanes` object[], required
              - …
          - `legacyKeyframes` object
            - `needsUpgrade` true, required
            - `channels` string[], required
            - `record` unknown, required
        - object
          - `id` string, required
          - `startTime` number, required
          - `duration` number, required
  - `markers` object[]
    - `id` string, required
    - `startTime` number, required
    - `endTime` number
    - `kind` string, required
    - `name` string
    - `metadata` object
  - `captions` union
    - object
      - `source` 'derived', required
      - `from` object, required
        - `clipId` string, required
      - `segments` object[], required
        - `words` object[], required
          - `text` string, required
          - `startMs` number, required: Word start on the composition timeline, in milliseconds.
          - `endMs` number, required: Word end on the composition timeline, in milliseconds.
          - `emphasis` boolean
          - `annotate` 'circle' | 'underline'
      - `style` object
        - `fontFamily` string
        - `fontSize` number: Font size as % of composition height (1-15, default 5). NOT px.
        - `fontWeight` integer
        - `color` string
        - `highlightColor` string
        - `backgroundColor` string
        - `plate` 'glass' | 'solid' | 'none'
        - `position` 'top' | 'center' | 'bottom'
        - `region` object: Optional caption layout box {x,y,w,h} in normalized output-frame coordinates. Overrides position; text starts at its top edge and wraps/pages within its width and height.
          - `x` number, required
          - `y` number, required
          - `w` number, required
          - `h` number, required
        - `textTransform` 'none' | 'uppercase' | 'capitalize'
        - `letterSpacing` number: Base caption glyph spacing in pixels.
        - `wordGapEm` number: Gap between caption runs as a multiple of base font size; default 0.35.
        - `lineGapEm` number: Gap between caption lines as a multiple of base font size; default 0.25.
        - `emphasisColor` string
        - `emphasisGradient` string
        - `emphasisScale` number
        - `emphasisFontFamily` string
        - `emphasisFontWeight` integer
        - `emphasisTextTransform` 'none' | 'uppercase' | 'capitalize'
        - `emphasisLetterSpacing` number
        - `emphasisFontStyle` 'normal' | 'italic'
        - `entrance` 'none' | 'fade' | 'word-pop' | 'stagger-up'
        - `entranceMs` number
        - `entranceStaggerMs` number
        - `exitMs` number
        - `emphasisPlate` boolean
        - `emphasisPlateColor` string
        - `emphasisPlateTextColor` string
        - `emphasisPlateAltColor` string
        - `emphasisPlateAltTextColor` string
        - `emphasisPlateRadius` number
        - `emphasisPlatePadX` number
        - `emphasisPlatePadY` number
        - `emphasisPlateReveal` boolean: Grow a shared emphasis plate as saved words appear while reserving its final layout width.
        - `annotationColor` string
        - `annotationStrokeWidth` number: Annotation stroke width in its 100×100 SVG coordinate system; default 4.
        - `annotationTexture` 'clean' | 'pencil': Native annotation stroke finish; default clean.
        - `maxWidthPercent` number
      - `layouts` object[]
        - `segmentIndex` integer, required
        - `wordStartIndex` integer, required
        - `wordEndIndex` integer, required: Exclusive word index in the saved caption segment.
        - `region` object, required
          - `x` number, required
          - `y` number, required
          - `w` number, required
          - `h` number, required
        - `scale` number
        - `rotation` number
        - `style` object
          - `fontFamily` string
          - `fontSize` number: Font size as % of composition height (1-15, default 5). NOT px.
          - `fontWeight` integer
          - `color` string
          - `highlightColor` string
          - `backgroundColor` string
          - `plate` 'glass' | 'solid' | 'none'
          - `textTransform` 'none' | 'uppercase' | 'capitalize'
          - `letterSpacing` number: Base caption glyph spacing in pixels.
          - `wordGapEm` number: Gap between caption runs as a multiple of base font size; default 0.35.
          - `lineGapEm` number: Gap between caption lines as a multiple of base font size; default 0.25.
          - `emphasisColor` string
          - `emphasisScale` number
          - `emphasisFontFamily` string
          - `emphasisFontWeight` integer
          - `emphasisTextTransform` 'none' | 'uppercase' | 'capitalize'
          - `emphasisLetterSpacing` number
          - `emphasisFontStyle` 'normal' | 'italic'
          - `emphasisPlate` boolean
          - `emphasisPlateColor` string
          - `emphasisPlateTextColor` string
          - `emphasisPlateAltColor` string
          - `emphasisPlateAltTextColor` string
          - `emphasisPlateRadius` number
          - `emphasisPlatePadX` number
          - `emphasisPlatePadY` number
          - `emphasisPlateReveal` boolean: Grow a shared emphasis plate as saved words appear while reserving its final layout width.
          - `annotationColor` string
          - `annotationStrokeWidth` number: Annotation stroke width in its 100×100 SVG coordinate system; default 4.
          - `annotationTexture` 'clean' | 'pencil': Native annotation stroke finish; default clean.
          - `entrance` 'none' | 'fade' | 'word-pop' | 'stagger-up'
          - `entranceMs` number
          - `entranceStaggerMs` number
          - `exitMs` number
        - `holdThroughWordIndex` integer: Inclusive saved-word index in this segment. The layout stays visible until that word ends.
        - `wordMarks` object[]
          - `wordIndex` integer, required
          - `emphasis` boolean
          - `annotate` 'circle' | 'underline', nullable
        - `wordSignature` string, required
      - `zPlane` 'front' | 'behind-subject'
    - object
      - `source` 'authored', required
      - `segments` object[], required
        - `words` object[], required
          - `text` string, required
          - `startMs` number, required: Word start on the composition timeline, in milliseconds.
          - `endMs` number, required: Word end on the composition timeline, in milliseconds.
          - `emphasis` boolean
          - `annotate` 'circle' | 'underline'
      - `style` object
        - `fontFamily` string
        - `fontSize` number: Font size as % of composition height (1-15, default 5). NOT px.
        - `fontWeight` integer
        - `color` string
        - `highlightColor` string
        - `backgroundColor` string
        - `plate` 'glass' | 'solid' | 'none'
        - `position` 'top' | 'center' | 'bottom'
        - `region` object: Optional caption layout box {x,y,w,h} in normalized output-frame coordinates. Overrides position; text starts at its top edge and wraps/pages within its width and height.
          - `x` number, required
          - `y` number, required
          - `w` number, required
          - `h` number, required
        - `textTransform` 'none' | 'uppercase' | 'capitalize'
        - `letterSpacing` number: Base caption glyph spacing in pixels.
        - `wordGapEm` number: Gap between caption runs as a multiple of base font size; default 0.35.
        - `lineGapEm` number: Gap between caption lines as a multiple of base font size; default 0.25.
        - `emphasisColor` string
        - `emphasisGradient` string
        - `emphasisScale` number
        - `emphasisFontFamily` string
        - `emphasisFontWeight` integer
        - `emphasisTextTransform` 'none' | 'uppercase' | 'capitalize'
        - `emphasisLetterSpacing` number
        - `emphasisFontStyle` 'normal' | 'italic'
        - `entrance` 'none' | 'fade' | 'word-pop' | 'stagger-up'
        - `entranceMs` number
        - `entranceStaggerMs` number
        - `exitMs` number
        - `emphasisPlate` boolean
        - `emphasisPlateColor` string
        - `emphasisPlateTextColor` string
        - `emphasisPlateAltColor` string
        - `emphasisPlateAltTextColor` string
        - `emphasisPlateRadius` number
        - `emphasisPlatePadX` number
        - `emphasisPlatePadY` number
        - `emphasisPlateReveal` boolean: Grow a shared emphasis plate as saved words appear while reserving its final layout width.
        - `annotationColor` string
        - `annotationStrokeWidth` number: Annotation stroke width in its 100×100 SVG coordinate system; default 4.
        - `annotationTexture` 'clean' | 'pencil': Native annotation stroke finish; default clean.
        - `maxWidthPercent` number
      - `layouts` object[]
        - `segmentIndex` integer, required
        - `wordStartIndex` integer, required
        - `wordEndIndex` integer, required: Exclusive word index in the saved caption segment.
        - `region` object, required
          - `x` number, required
          - `y` number, required
          - `w` number, required
          - `h` number, required
        - `scale` number
        - `rotation` number
        - `style` object
          - `fontFamily` string
          - `fontSize` number: Font size as % of composition height (1-15, default 5). NOT px.
          - `fontWeight` integer
          - `color` string
          - `highlightColor` string
          - `backgroundColor` string
          - `plate` 'glass' | 'solid' | 'none'
          - `textTransform` 'none' | 'uppercase' | 'capitalize'
          - `letterSpacing` number: Base caption glyph spacing in pixels.
          - `wordGapEm` number: Gap between caption runs as a multiple of base font size; default 0.35.
          - `lineGapEm` number: Gap between caption lines as a multiple of base font size; default 0.25.
          - `emphasisColor` string
          - `emphasisScale` number
          - `emphasisFontFamily` string
          - `emphasisFontWeight` integer
          - `emphasisTextTransform` 'none' | 'uppercase' | 'capitalize'
          - `emphasisLetterSpacing` number
          - `emphasisFontStyle` 'normal' | 'italic'
          - `emphasisPlate` boolean
          - `emphasisPlateColor` string
          - `emphasisPlateTextColor` string
          - `emphasisPlateAltColor` string
          - `emphasisPlateAltTextColor` string
          - `emphasisPlateRadius` number
          - `emphasisPlatePadX` number
          - `emphasisPlatePadY` number
          - `emphasisPlateReveal` boolean: Grow a shared emphasis plate as saved words appear while reserving its final layout width.
          - `annotationColor` string
          - `annotationStrokeWidth` number: Annotation stroke width in its 100×100 SVG coordinate system; default 4.
          - `annotationTexture` 'clean' | 'pencil': Native annotation stroke finish; default clean.
          - `entrance` 'none' | 'fade' | 'word-pop' | 'stagger-up'
          - `entranceMs` number
          - `entranceStaggerMs` number
          - `exitMs` number
        - `holdThroughWordIndex` integer: Inclusive saved-word index in this segment. The layout stays visible until that word ends.
        - `wordMarks` object[]
          - `wordIndex` integer, required
          - `emphasis` boolean
          - `annotate` 'circle' | 'underline', nullable
        - `wordSignature` string, required
      - `zPlane` 'front' | 'behind-subject'
  - `sourceSuggestionId` string
  - `license` object
    - `type` string, required
    - `source` string
    - `notes` string
  - `licenseVerification` 'attested' | 'cueframe-verified' | 'third-party-verified'
  - `accessibility` object
    - `audioDescriptionTrackId` string
    - `captionsTrackId` string
    - `signLanguageTrackId` string
    - `notes` string
  - `ownerOrgId` string
  - `acl` object
    - `public` boolean
    - `readerOrgIds` string[]
    - `writerOrgIds` string[]

## Response `200`

Validation verdict (ALWAYS 200 for a JSON body — both wire-shape and deep-invariant failures). `valid:false` lists authoring errors (envelopes) to fix before save/render; `valid:true` means the composition passes every pure save-time guard.

- object
  - `contract` string, required: The contract this verdict was computed against (the kernel version the deployment runs). If it differs between two calls, the validator changed, not your composition.
  - `valid` boolean, required
  - `errors` object[], required
    - `code` string, required
    - `stage` string, required
    - `category` string, required
    - `retryable` boolean, required
    - `message` string, required
    - `fix` object
  - `advisories` object[], required: Deterministic, render-free QUALITY floors (the SAME named defects the Director's in-loop verify and score_composition surface: static-hold, caption-spine, sfx-density, sfx-speech-collision, bed-coverage, audio-role). NON-blocking — they never flip `valid` (an editorially-weak composition still renders); fix them for quality, not to save. Empty when the composition is clean or the wire shape failed (they need a parsed composition).
    - `rule` string, required
    - `criterion` string, required
    - `clipId` string
    - `detail` string, required
    - `fix` string, required
  - `warnings` string[]: Non-blocking render-fidelity warnings detected before enqueue, including unavailable font families.
  - `quote` object, required: The A2 money triple for the commodity lane: validate is free; estimatedJourneyTotalUsd is the render price to finish from a valid composition — relay it BEFORE create_render.
    - `thisStepUsd` number, required
    - `jobSpendSoFarUsd` number, required
    - `estimatedJourneyTotalUsd` number, required

## Other responses

- `400`: Bad request — validation_error / invalid_cursor / invalid_query.
- `401`: Missing or invalid Authorization header.
- `402`: Payment required. Returned when the caller presents no API key on a priced operation: the body AND the `Payment-Required` header carry the x402 v2 challenge (accepted scheme, network, USDC asset, amount, payTo). Sign the EIP-3009 authorization and retry with a `PAYMENT-SIGNATURE` header. A KEYED caller sees this status instead when their plan lacks or has exhausted the feature (`billing_required`).
- `429`: Rate limited. Retry-After header present.
- `500`: Internal error.

---

[API](https://skmtc.dev/cueframe/apis/cueframe-api.md) · [All operations](https://skmtc.dev/cueframe/apis/cueframe-api/llms.txt) · [OpenAPI document](https://skmtc.dev/cueframe/apis/cueframe-api/revisions/8f9bfe362b4a?raw)
