CueFrame · Schema
CompositionApplyRequest
POST /projects/:id/composition/apply body — a batch of CompositionOps.
CompanyVideoVideo EditingVideo RenderingAI AgentsMCPMediaDeveloper Tools
Properties
| Name | Type | Description |
|---|---|---|
| ops | array | |
| componentExecutionVersions | object | |
| builtinSources | array | |
| expectedBrandIdentity | string | |
| concurrency | object | Version 1 shared dependency admission. Guarded requests must name the observed immutable composition with composition_id or concurrency.compositionId; changed or missing dependencies refuse the entire |
| if_match | string | OCC precondition: the strong etag (`" |
| dry_run | boolean | When true, validate the batch (apply + projection accept/reject guards) WITHOUT persisting. Returns `{ valid, errors }` instead of applying. |
| composition_id | string | Target a SPECIFIC composition instead of the project's active one. Omit it and the project's active composition is edited, which is the default and the common case. `preview_frame` and `create_render` |
| session_id | string | Optional compose-session id. When present, the ops target the session's SCRATCH composition (a derived sibling) instead of the project's active composition — the server never mutates active until comm |
JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://artifacts.apis.io/cueframe-ai/json-schema/cueframe-ai-composition-apply-request-schema.json",
"title": "CompositionApplyRequest",
"description": "POST /projects/:id/composition/apply body — a batch of CompositionOps.",
"x-generated": "2026-11-10",
"x-method": "derived",
"x-generator": "derive-json-schema.py",
"x-source": "openapi/cueframe-ai-compositions-api-openapi.yml#/components/schemas/CompositionApplyRequest",
"type": "object",
"properties": {
"ops": {
"minItems": 1,
"type": "array",
"items": {
"$ref": "#/$defs/CompositionOp"
}
},
"componentExecutionVersions": {
"anyOf": [
{
"type": "array",
"prefixItems": [
{
"type": "number",
"const": 3
}
],
"items": false,
"minItems": 1,
"maxItems": 1
},
{
"type": "array",
"prefixItems": [
{
"type": "number",
"const": 3
},
{
"type": "number",
"const": 4
}
],
"items": false,
"minItems": 2,
"maxItems": 2
}
]
},
"builtinSources": {
"minItems": 1,
"maxItems": 512,
"type": "array",
"items": {
"type": "object",
"properties": {
"scope": {
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "builtin"
},
"collection": {
"type": "string",
"minLength": 1,
"maxLength": 256
}
},
"required": [
"kind",
"collection"
],
"additionalProperties": false
},
"componentId": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"sourceVersionId": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"sourceVersion": {
"anyOf": [
{
"type": "number",
"const": 3
},
{
"type": "number",
"const": 4
}
]
}
},
"required": [
"scope",
"componentId",
"sourceVersionId",
"sourceVersion"
],
"additionalProperties": false
}
},
"expectedBrandIdentity": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"concurrency": {
"description": "Version 1 shared dependency admission. Guarded requests must name the observed immutable composition with composition_id or concurrency.compositionId; changed or missing dependencies refuse the entire batch.",
"$ref": "#/$defs/CompositionConcurrency"
},
"if_match": {
"description": "OCC precondition: the strong etag (`\"<hex>\"`) the caller last saw. Required unless `concurrency` is sent or `dry_run` is true; a write with neither is refused with 428 precondition_required. On mismatch the batch is rejected (409 stale_etag) before any op applies.",
"type": "string"
},
"dry_run": {
"description": "When true, validate the batch (apply + projection accept/reject guards) WITHOUT persisting. Returns `{ valid, errors }` instead of applying.",
"type": "boolean"
},
"composition_id": {
"description": "Target a SPECIFIC composition instead of the project's active one. Omit it and the project's active composition is edited, which is the default and the common case. `preview_frame` and `create_render` already accept a compositionId; this closes the gap so a second composition minted with `new_composition` can actually be authored into — build variant A, mint B, edit B, preview both. Mutually exclusive with `session_id`, which targets the session's scratch.",
"type": "string"
},
"session_id": {
"description": "Optional compose-session id. When present, the ops target the session's SCRATCH composition (a derived sibling) instead of the project's active composition — the server never mutates active until commit.",
"type": "string"
}
},
"required": [
"ops"
],
"additionalProperties": false,
"$defs": {
"ComponentExecutionRef": {
"oneOf": [
{
"type": "object",
"properties": {
"version": {
"type": "number",
"const": 3
},
"componentId": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"sourceVersionId": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"params": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"$ref": "#/$defs/ComponentParameterValue"
}
},
"seed": {
"type": "string",
"minLength": 1
},
"assets": {
"type": "object",
"propertyNames": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"additionalProperties": {
"type": "string",
"minLength": 1
}
},
"mediaBindings": {
"type": "object",
"propertyNames": {
"type": "string",
"minLength": 1,
"maxLength": 128
},
"additionalProperties": {
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 128
}
}
}
},
"required": [
"version",
"componentId",
"sourceVersionId",
"params",
"seed"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"version": {
"type": "number",
"const": 4
},
"componentId": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"sourceVersionId": {
"type": "string",
"pattern": "^[a-f0-9]{64}$"
},
"params": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"$ref": "#/$defs/ComponentParameterValue"
}
},
"seed": {
"type": "string",
"minLength": 1
},
"assets": {
"type": "object",
"propertyNames": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"additionalProperties": {
"type": "string",
"minLength": 1
}
},
"mediaBindings": {
"type": "object",
"propertyNames": {
"type": "string",
"minLength": 1,
"maxLength": 128
},
"additionalProperties": {
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 128
}
}
}
},
"required": [
"version",
"componentId",
"sourceVersionId",
"params",
"seed"
],
"additionalProperties": false
}
]
},
"ComponentParameterValue": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "null"
},
{
"type": "array",
"items": {
"$ref": "#/$defs/ComponentParameterValue"
}
},
{
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"$ref": "#/$defs/ComponentParameterValue"
}
}
]
},
"CompositionConcurrency": {
"oneOf": [
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "exact"
},
"expectedRevision": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
},
"required": [
"mode",
"expectedRevision"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "guarded"
},
"compositionId": {
"type": "string",
"minLength": 1,
"maxLength": 128
},
"observedRevision": {
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
},
"basis": {
"maxItems": 2048,
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"token": {
"type": "string",
"maxLength": 65536
}
},
"required": [
"key",
"token"
],
"additionalProperties": false
}
}
},
"required": [
"mode",
"observedRevision",
"basis"
],
"additionalProperties": false
}
]
},
"CompositionOp": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "clip.add"
},
"clip": {
"$ref": "#/$defs/TimelineClip"
},
"trackId": {
"type": "string"
},
"newTrack": {
"type": "boolean"
},
"newTrackId": {
"type": "string"
}
},
"required": [
"type",
"clip"
],
"additionalProperties": false,
"description": "Add a clip to the timeline. The clip self-routes to a track by FAMILY (`clip.kind`, else derived from `source.kind`, else the target track) — a `media` clip MUST carry `kind` (video|audio|image) or a `trackId`, else it is rejected as ambiguous. `trackId` targets an existing SAME-family track; `newTrack:true` (with optional pinned `newTrackId`) creates a fresh track of that family, appended at the BACK (the highest index, painted under every other track); raise it with `reorderTracks` (index 0 is the top layer)."
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "clip.remove"
},
"clipId": {
"type": "string"
}
},
"required": [
"type",
"clipId"
],
"additionalProperties": false,
"description": "Remove a clip by id (located across every track)."
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "clip.setKeyframes"
},
"clipId": {
"type": "string",
"minLength": 1
},
"property": {
"type": "string",
"enum": [
"opacity",
"scale",
"rotation",
"position.x",
"position.y",
"volume"
]
},
"keyframes": {
"anyOf": [
{
"minItems": 2,
"type": "array",
"items": {
"type": "object",
"properties": {
"time": {
"type": "number",
"minimum": 0
},
"value": {
"type": "number"
},
"ease": {
"type": "string"
}
},
"required": [
"time",
"value"
],
"additionalProperties": false
}
},
{
"type": "null"
}
]
}
},
"required": [
"type",
"clipId",
"property",
"keyframes"
],
"additionalProperties": false,
"description": "Set (or CLEAR, with `keyframes: null`) ONE keyframed channel of a clip: `opacity`, `scale`, `rotation`, `position.x`, `position.y` or `volume`, with at least two keyframes `{time, value, ease?}` in any order. `time` is seconds from the clip's own start (0 to its duration); `value` uses the property's own unit, as its scalar: opacity 0-1, scale a multiplier, rotation degrees, position.x/position.y a FRACTION of the frame (0.5 = centre, never px), volume 0-4. Each keyframe's optional `ease` shapes the segment ARRIVING at it, in HyperFrames' vocabulary: `none` (linear, the default), named eases such as `power2.out`, `back.out`, `expo.inOut`, `spring-gentle`, `hold`, or `custom(M0,0 Cx1,y1 x2,y2 1,1)` (a cubic, x in [0,1]). Before its first keyframe a channel holds its first value, after its last it holds the last; a channel OVERRIDES the clip's scalar (`opacity`, `scale`, `rotation`, `position`). The op REPLACES that property's keyframes; other channels are untouched. Volume keyframes are linear and only for a video or audio media clip. Trims and splits keep every keyframe on its content. Keyframes are stored as HyperFrames tweens (`tweens`) and automation (`automation`) on the clip; author them with this op, not `clip.update`."
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "clip.update"
},
"clipId": {
"type": "string"
},
"patch": {
"type": "object",
"properties": {
"id": {
"type": "string",
"minLength": 1
},
"startTime": {
"type": "number",
"minimum": 0
},
"duration": {
"type": "number",
"exclusiveMinimum": 0
},
"source": {
"oneOf": [
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"const": "media"
},
"mediaId": {
"type": "string"
},
"trim": {
"type": "object",
"properties": {
"start": {
"type": "number",
"minimum": 0
},
"end": {
"type": "number",
"minimum": 0
}
},
"required": [
"start",
"end"
]
},
"excludedRanges": {
"type": "array",
"items": {
"type": "object",
"properties": {
"start": {
"type": "number",
"minimum": 0
},
"end": {
"type": "number",
"minimum": 0
},
"reason": {
"type": "string"
}
},
"required": [
"start",
"end"
]
}
},
"volume": {
"type": "number",
"minimum": 0,
"maximum": 4
},
"muted": {
"type": "boolean"
},
"playbackRate": {
"type": "number",
"exclusiveMinimum": 0
},
"sync": {
"type": "string",
"enum": [
"video",
"independent"
]
},
"kenBurns": {
"description": "Still-image motion. When unset, an image clip slowly pushes in (`in`) unless its fit is `contain` or it keyframes scale, position or rotation, so a keyframed clip renders exactly its keyframed values; set `none` for a still that holds.",
"type": "string",
"enum": [
"in",
"out",
"pan-l",
"pan-r",
"none"
]
},
"volumeDb": {
"type": "number"
},
"fadeInMs": {
"type": "number",
"minimum": 0
},
"fadeOutMs": {
"type": "number",
"minimum": 0
},
"loop": {
"type": "boolean"
},
"audioRole": {
"description": "Mix role. A role sets the clip's default gain when it has no `volume` or `volumeDb`: vo 0 dB, sfx -6 dB, music -8 dB (-16 dB when any vo clip exists), ambience -22 dB. Set `volumeDb: 0` to keep a role and play the source at its own level.",
"type": "string",
"enum": [
"sfx",
"music",
"vo",
"ambience"
]
},
"duck": {
"type": "object",
"properties": {
"duckDb": {
"type": "number",
"maximum": 0
},
"attackMs": {
"type": "number",
"minimum": 0
},
"releaseMs": {
"type": "number",
"minimum": 0
},
"windows": {
"type": "array",
"items": {
"type": "object",
"properties": {
"startMs": {
"type": "number"
},
"endMs": {
"type": "number"
}
},
"required": [
"startMs",
"endMs"
]
}
}
},
"required": [
"duckDb",
"attackMs",
"releaseMs"
]
},
"anchor": {
"type": "object",
"properties": {
"clipId": {
"type": "string",
"minLength": 1
},
"offsetMs": {
"type": "number"
}
},
"required": [
"clipId",
"offsetMs"
]
},
"reframe": {
"type": "object",
"properties": {
"segments": {
"minItems": 1,
"type": "array",
"items": {
"type": "object",
"properties": {
"startSec": {
"type": "number",
"minimum": 0,
"description": "Segment START in seconds, clip-relative. Segments tile the clip's duration in order."
},
"endSec": {
"type": "number",
"exclusiveMinimum": 0,
"description": "Segment END in seconds, clip-relative. The span (endSec − startSec) is this segment's duration — the window `ease` ramps over."
},
"focus": {
"oneOf": [
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "frame-center"
}
},
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "point"
},
"x": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"y": {
"type": "number",
"minimum": 0,
"maximum": 1
}
},
"required": [
"mode",
"x",
"y"
]
},
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "face"
},
"faceId": {
"type": "string",
"minLength": 1
},
"shotScale": {
"type": "string",
"enum": [
"close",
"standard",
"wide"
]
}
},
"required": [
"mode",
"faceId"
]
},
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "all-faces"
}
},
"required": [
"mode"
]
},
{
"type": "object",
"properties": {
"mode": {
"type": "string",
"const": "active-speaker"
}
},
"required": [
"mode"
]
}
],
"description": "WHO/WHAT the camera tracks this segment (the reframe center), selected by `mode`: `frame-center` = no tracking, hold the geometric center (static/graphic footage, or a pure zoom); `point` = lock to a fixed source-normalized {x,y} (a non-moving on-screen element); `face` = track a detected face by `faceId` (from get_media_context), with optional `shotScale` close|standard|wide for tightness; `all-faces` = frame the bounding box of every detected face (group/ensemble shots); `active-speaker` = follow whoever is speaking (diarized talking-head / podcast). Pick by what the beat is about; pair with `zoom` for how tight."
},
"intent": {
"description": "Optional editorial LABEL for diff/log readability (e.g. 'speaker-tight', 'wide-context'). Cosmetic — NOT honored by the derive math and excluded from the variant cache key.",
"type": "string"
},
"zoom": {
"description": "How TIGHT the framing is — the fraction of the source the output viewport shows. <1 punches IN (a smaller source fraction, magnified to fill the frame); 1 = the full frame (no crop). ~0.6–0.8 is a moderate push-in, ~0.5 a tight close-up, 0.1 the maximum zoom. There is NO zoom-OUT past the full frame (max 1). Omit for the full frame. MUTUALLY EXCLUSIVE with `focus.shotScale`: a face-tracked shot is sized EITHER by the editorial scale (shotScale, calibrated to the detected face) OR by an explicit zoom — never both.",
"type": "number",
"minimum": 0.1,
"maximum": 1
},
"ease": {
"description": "Zoom/center ramp DURATIONS in SECONDS at this segment's edges — NOT a unitless 0–1 curve. ~0.3–0.6s reads as a snappy push; >1s reads sluggish. Keep `in` + `out` ≤ the segment's duration (endSec − startSec): the two edges share the span, so to ramp across the WHOLE segment set one edge to the full duration and the other to 0. Omit for an instant cut to this framing.",
"type": "object",
"properties": {
"in": {
"type": "number",
"minimum": 0,
"description": "Ease-IN ramp DURATION in seconds at the segment START."
},
"out": {
"type": "number",
"minimum": 0,
"description": "Ease-OUT ramp DURATION in seconds at the segment END."
}
},
"required": [
"in",
"out"
]
},
"bias": {
"description": "Optional FINE framing nudge off the focus center, source-normalized in [−0.3, 0.3]. A small correction (headroom, rule-of-thirds), NOT a reframe — use `focus`/`zoom` for the actual shot. Omit (treated as 0) to sit dead-center on the focus.",
"type": "object",
"properties": {
"x": {
"type": "number",
"minimum": -0.3,
"maximum": 0.3,
"description": "Horizontal nudge, source-normalized (+ = right)."
},
"y": {
"type": "number",
"minimum": -0.3,
"maximum": 0.3,
"description": "Vertical nudge, source-normalized (+ = down)."
}
},
"required": [
# --- truncated at 32 KB (326 KB total) ---
# Full source: https://artifacts.apis.io/cueframe-ai/json-schema/cueframe-ai-composition-apply-request-schema.json
Work with this as data
Every JSON Schema here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for schemas
4 MCP tools reach this
find_json_schemasBrowse and filter every JSON Schema in the catalog.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.
Call it yourself
curl for this page
This JSON Schema
curl "https://apis.io/api/v1/json-schemas/cueframe-ai-composition-apply-request"
All schemas
curl "https://apis.io/api/v1/json-schemas?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.