---
title: "List projects"
method: GET
path: "/v1/projects"
tags: ["projects"]
---

# List projects

`GET /v1/projects`

## Request

- Base URL: `https://api.framelane.io`
- URL: `https://api.framelane.io/v1/projects`
- Auth: HTTP bearer

Cursor-paginated projects for the workspace. Each item is a compact summary. `kind` defaults to `project`, so templates are listed only with `kind=template` or `kind=all`.

## Query parameters

- `kind` 'project' | 'template' | 'all'
- `limit` integer
- `cursor` string, nullable

## Response `200`

Successful Response

- CursorPageProjectOut
  - `data` ProjectOut[], required
    - `id` string, required: Unique project id.
    - `name` string, required: Display name.
    - `version` integer, required: Head version. Bumped on every applied ops batch.
    - `workspace_id` string, required: Owning workspace id.
    - `editor_url` string, required: Open this project in the authenticated console.
    - `kind` 'project' | 'template': `project`, or `template`: a project used as a copy source.
    - `source_project_id` string, nullable: The project or template this one was copied from.
    - `source_kind` 'project' | 'template' | 'public_template', nullable: What the source was: `project` (a duplicate), `template` (one of your templates) or `public_template` (a published template from the gallery).
    - `thumbnail_url` string, nullable: A poster image of the head; null until one is generated.
    - `thumbnail_version` integer, nullable: The head version the thumbnail shows. It is stale while this is below `version`.
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required
    - `summary` ProjectSummary, required: A token-light view of a composition: canvas + one line per element.
      - `width` integer, nullable
      - `height` integer, nullable
      - `duration` number, nullable
      - `frame_rate` integer, nullable
      - `output_format` string, nullable
      - `element_count` integer: Elements at every depth: the root's, each frame's children and each instance's slot content.
      - `transition_count` integer
      - `frame_count` integer: Frames, at every depth.
      - `instance_count` integer: Instances, at every depth.
      - `operation_count` integer: Entries in the request's `operations` groups.
      - `component_count` integer: Component definitions.
      - `deepest` string[]: Ids from a root element down to the most deeply nested element (the first in document order); empty when nothing nests.
      - `renders_count` integer, nullable: Number of billable renders submitted for this project. Null when not computed.
      - `elements` ProjectElementSummary[]
        - `id` string, nullable: Element id (the op target).
        - `parent` string, nullable: The frame or instance (for its slot content) it sits in; null at the root.
        - `type` string, required: Element type: video, audio, text, image, etc.
        - `time` number, nullable: Start time on the timeline, in seconds.
        - `duration` number, nullable: On-timeline duration, when derivable.
        - `label` string, nullable: Human label (name / text), truncated.
        - `source` string, nullable: Source filename for media elements.
    - `render_request` RenderRequest: Body for ``POST /v1/renders``. Defines the composition to render: dimensions, output format, timeline elements, and optional transitions. The render engine produces a single video artifact.
      - `version` 'v4' | 'v5', nullable: Wire version of this request. Omit it: the render task is v5 exactly when the request carries a v5 block, and v4 otherwise. `v5` accepts the v5 blocks (a shape's `geometry`, `frame` and `instance` elements, `operations`, `components`); `v4` refuses them.
      - `schema_version` 'v5', nullable: The document format an editor wrote this request in. Not sent to the renderer; `version` is the wire's.
      - `width` integer, nullable: Output width in pixels. Must be set together with `height` or both omitted.
      - `height` integer, nullable: Output height in pixels. Must be set together with `width` or both omitted.
      - `duration` number, nullable: Total composition duration in seconds. Inferred from elements when omitted.
      - `frame_rate` integer, nullable: Frames per second. When set, the output encodes at exactly this rate. Defaults to 30 when omitted.
      - `output_format` 'mp4' | 'webm' | 'mov' | 'gif' | 'png' | 'jpg': Values: - `mp4` — H.264 MP4 video — the default, widely compatible delivery format. - `webm` — VP9 WebM video — supports a transparent (alpha) background. - `mov` — QuickTime MOV — ProRes 4444 with a transparent (alpha) background; large files. - `gif` — Animated GIF — roadmap; not yet emitted by the renderer. - `png` — Single-frame PNG still — roadmap; not yet emitted by the renderer. - `jpg` — Single-frame JPEG still — roadmap; not yet emitted by the renderer.
      - `output_filename` string, nullable: Custom filename for the artifact (without extension). Auto-generated when omitted.
      - `background_color` string: RGBA background color in hex format (#RRGGBBAA).
      - `background_image_url` string, uri, nullable: URL of an image to use as the composition background.
      - `alpha` boolean: When true, produce an alpha-channel (transparent background) output. Requires `output_format=webm` or `output_format=mov`.
      - `elements` union[]: Ordered list of timeline elements (video, image, text, audio, etc.).
        - union
          - VideoElement
            - `lut_url` string, uri, nullable
            - `lut_intensity` number
            - `brightness` number: Brightness, -100..100, applied as a 0..2 colour multiplier (0 = no change). `-100` multiplies by 0: the element renders pure black while keeping its alpha — a silhouette of the artwork. Combine a `brightness: -100` copy of an image with `blur`, reduced `opacity` and a small offset, stacked beneath the real one, to fake the content-shaped drop shadow `shadow_x`/`shadow_y` cannot produce.
            - `contrast` number
            - `saturation` number
            - `exposure` number
            - `sharpness` number
            - `blur` number
            - `noise` number
            - `vignette` number
            - `hue_rotate` number
            - `temperature` number: White balance warm (+) / cool (−), −1..1; 0 = no-op. Applied as a channel matrix, after the classic brightness/contrast chain.
            - `tint` number: White balance magenta (+) / green (−), −1..1; 0 = no-op.
            - `vibrance` number: Saturation boost weighted toward muted colours, −1..1; 0 = no-op. Boosts flat colour without wrecking skin tones.
            - `highlights` number: Luma-masked tone lift of the brightest range, −1..1; 0 = no-op.
            - `shadows` number: Luma-masked tone lift of the darkest range, −1..1; 0 = no-op.
            - `crop_top` number
            - `crop_bottom` number
            - `crop_left` number
            - `crop_right` number
            - `border_radius` union: Corner radius in pixels — a single value, or per-corner `{top_left, top_right, bottom_right, bottom_left}` for asymmetric corners (e.g. a card rounded only on top). The renderer normalizes px to a 0–1 fraction of the element's shorter side (≈ half the shorter side is fully rounded) and clamps to that range. For shape elements use `corner_radius` (already 0–1).
              - …
            - `border_color` string, nullable
            - `border_width` union: Border thickness in pixels (converted to the renderer's relative border scale against the element's shorter side, so it survives resizes). The border outlines the element's border_radius-rounded quad — not the image's alpha silhouette.
              - …
            - `shadow_color` string, nullable
            - `shadow_blur` union: Shadow softness, 0–100 (renderer blur-strength scale, not pixels): 0 = hard edge, 100 = maximum blur. Values are clamped to that range.
              - …
            - `shadow_x` union: Horizontal shadow offset in pixels (converted to the renderer's relative offset scale). The shadow silhouette is the element's border_radius-rounded quad, not the content alpha — a cut-out PNG gets a card shadow, not an outline-shaped one. On a cut-out that reads as a floating rectangle (rotated with the element, which is the tell). For a shadow that follows the artwork, drop these fields and stack a second copy of the same image underneath at `brightness: -100` (a black silhouette, alpha preserved) with `blur` and reduced `opacity`, offset a few percent — see `brightness`.
              - …
            - `shadow_y` union: Vertical shadow offset in pixels. See `shadow_x`.
              - …
            - `blend_mode` 'none' | 'multiply' | 'screen' | 'overlay' | 'darken' | 'lighten' | 'color_dodge' | 'color_burn' | 'hard_light' | 'soft_light' | 'difference' | 'add' | 'exclusion' | 'hue' | 'saturation' | 'color' | 'luminosity': Values: - `none` — Normal compositing (source-over) — the default. - `multiply` — Multiply blend — darkens by multiplying layer colors. - `screen` — Screen blend — lightens by inverting, multiplying, inverting. - `overlay` — Overlay blend — multiply in shadows, screen in highlights. - `darken` — Darken blend — keeps the darker of the two layers per channel. - `lighten` — Lighten blend — keeps the lighter of the two layers per channel. - `color_dodge` — Color-dodge blend — brightens the base toward the blend color. - `color_burn` — Color-burn blend — darkens the base toward the blend color. - `hard_light` — Hard-light blend — overlay with layers swapped. - `soft_light` — Soft-light blend — a gentler hard-light. - `difference` — Difference blend — absolute difference of the two layers per channel. - `add` — Additive (linear-dodge) blend — sums layer colors, clamped. - `exclusion` — Exclusion blend — like difference, lower contrast. - `hue` — Hue blend — backdrop luminosity + saturation with the blend layer's hue. - `saturation` — Saturation blend — backdrop hue + luminosity with the blend layer's saturation. - `color` — Color blend — backdrop luminosity with the blend layer's hue + saturation. - `luminosity` — Luminosity blend — backdrop hue + saturation with the blend layer's luminosity.
            - `mask_shape` 'circle' | 'diamond' | 'hexagon' | 'star' | 'heart' | 'triangle': Analytic (SDF) shape mask that clips an element's coverage. §1. Values: - `circle` — Circular (ellipse) analytic mask, fit to the element bounds. - `diamond` — Diamond (rotated square) analytic mask. - `hexagon` — Regular hexagon analytic mask. - `star` — Five-point star analytic mask. - `heart` — Heart analytic mask. - `triangle` — Upward triangle analytic mask.
            - `mask` MaskConfig: Animated/feathered mask params layered on the analytic `mask_shape` (§5c).
              - …
            - `mask_keyframes` MaskKeyframe[], nullable
              - …
            - `matte` Matte: Track matte (§5d): drive this element's coverage from another layer's render.
              - …
            - `backdrop_blur` union: Glass: the scene behind the element, Gaussian-blurred, fills its footprint under its translucent content (a shape's own silhouette, a video's or image's box with its corner radius and mask). A Gaussian sigma in canvas px, as CSS `backdrop-filter: blur()` takes it; the renderer caps it at 128. 0 is off.
              - …
            - `backdrop_blur_keyframes` BlurKeyframe[], nullable: Animate `backdrop_blur` over the timeline; replaces the static value while present.
              - …
            - `layer_blur` number: Blurs the element itself as drawn: a Gaussian sigma in canvas px, as CSS `filter: blur()` takes it, spreading three sigma past its box. At most 128; 0 is off. Not on a shape yet (the editor's preview cannot draw it), and not with `blend_mode`, `backdrop_blur` or `matte`, beside which the renderer would draw the element without it.
            - `layer_blur_keyframes` BlurKeyframe[], nullable: Animate `layer_blur` over the timeline; replaces the static value while present.
              - …
            - `glass` Glass: A refracting glass surface (V6, render-node AEC §1): within `bevel` px of the element's edge (a shape's own outline, a box's rounded corners) the scene behind is read inward, bent by `refraction`, its red and blue split by `dispersion`. With `backdrop_blur` it bends the blurred scene, without it the sharp one.
              - …
            - `x` union
              - …
            - `y` union
              - …
            - `width` union: Box width. Together with `height` this is a **cover** box, not a fit box: sized media (video, image) scales to fill it and the overflow is **cropped** — it is never letterboxed. A box whose aspect ratio differs from the source's therefore silently cuts the source's edges. To show a source whole, match the box aspect to it: on a `W`x`H` canvas, for a source of aspect `a`, `width% = a * height% * (H/W)` — e.g. a 1024x1536 asset (a=0.667) on 1920x1080 needs `width% = 0.375 * height%`. Percentages are of the canvas, not the parent.
              - …
            - `height` union: Box height. See `width` — the pair is a cover box and crops rather than letterboxes. Percentages are of the canvas.
              - …
            - `aspect_ratio` number, nullable: **Accepted but ignored — the renderer never receives this field.** It does NOT constrain or correct the `width`/`height` box, so it cannot be used to stop a mismatched box from cropping the source; size the box per `width` instead. Kept for backward compatibility.
            - `x_anchor` union: Horizontal pivot the element **rotates** about, measured across its own box (`0%` = left edge, `50%` = centre, `100%` = right edge; px values are an offset from the left edge). This is a pivot, not an alignment — `x` always places the element's centre, so the anchor changes nothing on an unrotated element. Honored on video, image and shape; text and the generator elements rotate about their centre and reject a non-default anchor.
              - …
            - `y_anchor` union: Vertical pivot the element **rotates** about, measured across its own box (`0%` = top edge, `50%` = centre, `100%` = bottom edge). Same rules as `x_anchor`: a pivot, not an alignment.
              - …
            - `x_rotation` union
              - …
            - `y_rotation` union
              - …
            - `z_rotation` union
              - …
            - `x_scale` union
              - …
            - `y_scale` union
              - …
            - `flip_horizontal` boolean
            - `flip_vertical` boolean
            - `opacity` number
            - `z_index` integer, nullable: Stacking order (higher = on top). When omitted, elements stack in array order — later elements render above earlier ones (painter's rule); text defaults one layer above non-text. Set explicit values only when array order isn't the order you want.
            - `clip` boolean
            - `color_overlay` string, nullable: **Not supported by the renderer — setting this is rejected.** A strict submit returns `unsupported_feature` (422) and a dry-run reports it as a blocking violation; there is no native tint field to map it onto. To darken an element use `brightness` (`-100` is a black silhouette); to lay a colour over one, stack a `shape` with a flat or gradient `fill` above it.
            - `type` 'video'
            - `id` string, nullable: Unique identifier for this element. **Required on image elements** — an empty or missing ID causes the image to be silently skipped by the renderer. Recommended on all elements that are referenced by a transition.
            - `name` string, nullable: Optional human-readable label. Not used by the renderer.
            - `track` integer, nullable: Timeline track index (0–255). Informational only; not used by the renderer.
            - `time` number, nullable: When this element starts on the output timeline, in seconds. Defaults to `0` when omitted.
            - `visible` boolean: Set to `false` to skip this element without removing it from the request.
            - `locked` boolean: Marks the element locked in editors. Not sent to the renderer.
            - `source_url` string, uri, required: URL of the video file — MP4, MOV or WebM only; any other extension is rejected at submit. Must be accessible by the renderer.
            - `in_point` number, nullable: In point — seconds into the source file to start playing from. Defaults to `0`.
            - `out_point` number, nullable: Out point — seconds into the source file to stop playing. Defaults to end of source when omitted. Controls clip length on the timeline.
            - `speed` number: Playback speed. `1.0` = normal speed, `2.0` = 2× (half duration), `0.5` = half speed (double duration).
            - `playback_rate_keyframes` RateKeyframe[], nullable: Speed ramp (§5c): piecewise-linear playback-rate keyframes (absolute timeline seconds; rate 0 = freeze; at most 512 keyframes — the renderer truncates beyond that). Audio keeps the static rate — mute or detach it on ramped clips.
              - …
            - `volume` number: Embedded audio volume as a percentage (0–100). Affects only the audio track inside this video clip.
            - `muted` boolean: Silence the clip's embedded audio. `volume` is kept, so unmuting restores the level.
            - `loop` boolean: Repeat the source to fill the clip: when playback passes the end of the file it restarts from the file's first frame (not `in_point`). Set `out_point` past the source's length to choose how long the clip runs — its timeline length is still `(out_point − in_point) / speed`. Requires `out_point`.
            - `fade_in_duration` number: Duration in seconds of a linear audio fade-in at the clip's start.
            - `fade_out_duration` number: Duration in seconds of a linear audio fade-out at the clip's end.
            - `effects` Effect[]
              - …
            - `motion` Motion[]
              - …
          - AudioElement
            - `type` 'audio'
            - `id` string, nullable: Unique identifier for this element. **Required on image elements** — an empty or missing ID causes the image to be silently skipped by the renderer. Recommended on all elements that are referenced by a transition.
            - `name` string, nullable: Optional human-readable label. Not used by the renderer.
            - `track` integer, nullable: Timeline track index (0–255). Informational only; not used by the renderer.
            - `time` number, nullable: When this element starts on the output timeline, in seconds. Defaults to `0` when omitted.
            - `visible` boolean: Set to `false` to skip this element. Audio has no picture — `false` drops the whole track from the render, it does not mute or hide it (the linter reports that as `AUDIO_HIDDEN`). To keep the track but silence it, set `muted: true`.
            - `locked` boolean: Marks the element locked in editors. Not sent to the renderer.
            - `source_url` string, uri, required: URL of the audio file. Must be accessible by the renderer.
            - `in_point` number, nullable: In point — seconds into the source file to start from. Defaults to `0`.
            - `out_point` number, nullable: Out point — seconds into the source file to stop. Defaults to end of source when omitted. Controls clip length on the timeline.
            - `speed` number: Playback speed. `1.0` = normal speed, `2.0` = 2× speed.
            - `volume` number: Audio volume as a percentage (1–100). **Must be greater than 0** — the renderer silently drops audio streams with `volume <= 0`. Use `muted` to silence the track.
            - `muted` boolean: Silence this track while keeping it, and its `volume`, in the composition. A muted track is left out of the render's audio.
            - `fade_in_duration` number: Duration in seconds of a linear audio fade-in at the clip's start.
            - `fade_out_duration` number: Duration in seconds of a linear audio fade-out at the clip's end.
          - TextElement
            - `border_radius` union: Corner radius in pixels — a single value, or per-corner `{top_left, top_right, bottom_right, bottom_left}` for asymmetric corners (e.g. a card rounded only on top). The renderer normalizes px to a 0–1 fraction of the element's shorter side (≈ half the shorter side is fully rounded) and clamps to that range. For shape elements use `corner_radius` (already 0–1).
              - …
            - `border_color` string, nullable
            - `border_width` union: Border thickness in pixels (converted to the renderer's relative border scale against the element's shorter side, so it survives resizes). The border outlines the element's border_radius-rounded quad — not the image's alpha silhouette.
              - …
            - `shadow_color` string, nullable
            - `shadow_blur` union: Shadow softness, 0–100 (renderer blur-strength scale, not pixels): 0 = hard edge, 100 = maximum blur. Values are clamped to that range.
              - …
            - `shadow_x` union: Horizontal shadow offset in pixels (converted to the renderer's relative offset scale). The shadow silhouette is the element's border_radius-rounded quad, not the content alpha — a cut-out PNG gets a card shadow, not an outline-shaped one. On a cut-out that reads as a floating rectangle (rotated with the element, which is the tell). For a shadow that follows the artwork, drop these fields and stack a second copy of the same image underneath at `brightness: -100` (a black silhouette, alpha preserved) with `blur` and reduced `opacity`, offset a few percent — see `brightness`.
              - …
            - `shadow_y` union: Vertical shadow offset in pixels. See `shadow_x`.
              - …
            - `x` union
              - …
            - `y` union
              - …
            - `width` union: Box width. Together with `height` this is a **cover** box, not a fit box: sized media (video, image) scales to fill it and the overflow is **cropped** — it is never letterboxed. A box whose aspect ratio differs from the source's therefore silently cuts the source's edges. To show a source whole, match the box aspect to it: on a `W`x`H` canvas, for a source of aspect `a`, `width% = a * height% * (H/W)` — e.g. a 1024x1536 asset (a=0.667) on 1920x1080 needs `width% = 0.375 * height%`. Percentages are of the canvas, not the parent.
              - …
            - `height` union: Box height. See `width` — the pair is a cover box and crops rather than letterboxes. Percentages are of the canvas.
              - …
            - `aspect_ratio` number, nullable: **Accepted but ignored — the renderer never receives this field.** It does NOT constrain or correct the `width`/`height` box, so it cannot be used to stop a mismatched box from cropping the source; size the box per `width` instead. Kept for backward compatibility.
            - `x_anchor` union: Horizontal pivot the element **rotates** about, measured across its own box (`0%` = left edge, `50%` = centre, `100%` = right edge; px values are an offset from the left edge). This is a pivot, not an alignment — `x` always places the element's centre, so the anchor changes nothing on an unrotated element. Honored on video, image and shape; text and the generator elements rotate about their centre and reject a non-default anchor.
              - …
            - `y_anchor` union: Vertical pivot the element **rotates** about, measured across its own box (`0%` = top edge, `50%` = centre, `100%` = bottom edge). Same rules as `x_anchor`: a pivot, not an alignment.
              - …
            - `x_rotation` union
              - …
            - `y_rotation` union
              - …
            - `z_rotation` union
              - …
            - `x_scale` union
              - …
            - `y_scale` union
              - …
            - `flip_horizontal` boolean
            - `flip_vertical` boolean
            - `opacity` number
            - `z_index` integer, nullable: Stacking order (higher = on top). When omitted, elements stack in array order — later elements render above earlier ones (painter's rule); text defaults one layer above non-text. Set explicit values only when array order isn't the order you want.
            - `clip` boolean
            - `color_overlay` string, nullable: **Not supported by the renderer — setting this is rejected.** A strict submit returns `unsupported_feature` (422) and a dry-run reports it as a blocking violation; there is no native tint field to map it onto. To darken an element use `brightness` (`-100` is a black silhouette); to lay a colour over one, stack a `shape` with a flat or gradient `fill` above it.
            - `type` 'text'
            - `id` string, nullable: Unique identifier for this element. **Required on image elements** — an empty or missing ID causes the image to be silently skipped by the renderer. Recommended on all elements that are referenced by a transition.
            - `name` string, nullable: Optional human-readable label. Not used by the renderer.
            - `track` integer, nullable: Timeline track index (0–255). Informational only; not used by the renderer.
            - `time` number, nullable: When this element starts on the output timeline, in seconds. Defaults to `0` when omitted.
            - `visible` boolean: Set to `false` to skip this element without removing it from the request.
            - `locked` boolean: Marks the element locked in editors. Not sent to the renderer.
            - `duration` number, required: **Required.** Duration in seconds the text is visible. Omitting this field produces a zero-length window and the element will not appear in the render.
            - `text` string, required: The text content to render.
            - `font_family` string: Font family name. Must be available to the renderer.
            - `font_size` number: Font size in pixels.
            - `font_weight` integer: Font weight (100–900). The renderer has no variable weights: `600` and above render **bold**, everything below renders regular. Composes with `font_style` (italic + ≥600 = bold italic).
            - `font_style` 'normal' | 'italic' | 'bold' | 'bolditalic': Font style variant.
            - `text_color` string: Text fill color as a hex string.
            - `text_align` 'left' | 'center' | 'right': Horizontal text alignment.
            - `text_direction` 'ltr' | 'rtl': Text direction. `rtl` for right-to-left scripts (§4).
            - `text_decoration` 'none' | 'underline' | 'strikethrough': Text decoration. Only `none` renders. `underline` is reachable only per word — `words[].style.underline` — and `strikethrough` has no renderer path at all; both are rejected here (422) rather than silently dropped. Draw a strike-through as a thin `shape` rectangle over the text.
            - `tracking` number: Letter spacing (tracking) in pixels (converted to the renderer's width-relative unit on the wire). Positive values add space between characters.
            - `tabular_numbers` boolean: Tabular (monospaced) figures: every digit takes the same advance, so columns of numbers align across lines and a `counter` does not jitter as its value changes. The renderer's baked fonts carry the OpenType alternates; a font whose face lacks the feature gets synthesised tabular digits in the export only. Default `false` = the font's proportional figures.
            - `leading` number: Line height (leading) as a multiplier of font size. `1.2` = 20% taller than the font size.
            - `stroke_color` string, nullable: Outline color. Set together with `stroke_width` to enable text outlines.
            - `stroke_width` number: Outline width, em-relative (fraction of `font_size` — e.g. `0.05` = 5% of the font size). Has no effect unless `stroke_color` is also set.
            - `background_color` string, nullable: Secondary colour. Fills the box behind the text when `background` is `true`, **and** is the highlight colour for `word_animation.style` `color` (the active word) and `box` (the box) — those two styles require it.
            - `background_opacity` number
            - `x_padding` union
              - …
            - `y_padding` union
              - …
            - `background` boolean: Solid color box behind the full text block. Set `background_color` to choose the color.
            - `background_style` 'block' | 'line_hard' | 'line_round': Shape of the `background` plate: `block` — one box behind the whole text; `line_hard` — a square-cornered box behind each line; `line_round` — a rounded box behind each line. Requires `background: true`. An animation that centres its words draws the `block` plate whichever style is set.
            - `stroke` boolean: Outline-only mode — draws the glyph stroke with no fill. Requires `stroke_color` and `stroke_width`. When `false`, setting `stroke_color` + `stroke_width` gives fill + stroke (outlined).
            - `shadow` boolean: Built-in drop shadow.
            - `motion` Motion[]
              - …
            - `word_animation` WordAnimation
              - …
            - `words` WordSpec[], nullable: Per-word timing and optional per-word style overrides (§4). Emitted as a top-level `words[]` array; use alongside `motion`/`animation_preset` for the animation. Editorial-emphasis captions = word timings + a styled keyword.
              - …
            - `counter` ValueCounter: Rewrite a text element from an eased numeric sweep (the animated stat hero, §4).
              - …
            - `glow` TextGlow: Soft glow around the glyphs (§4).
              - …
            - `text_wrap` 'wrap' | 'nowrap'
            - `animation_preset` string, nullable: Renderer text-animation name (e.g. `'typewriter'`) — a catalog text animation or a `custom_animations.text` entry. Runs for the whole element window with per-glyph timings computed by the renderer. Use `motion[]` (with `type`, `preset` or `custom`) for structured timing control — but not both. Ignored when `word_animation` is set.
          - ImageElement
            - `lut_url` string, uri, nullable
            - `lut_intensity` number
            - `brightness` number: Brightness, -100..100, applied as a 0..2 colour multiplier (0 = no change). `-100` multiplies by 0: the element renders pure black while keeping its alpha — a silhouette of the artwork. Combine a `brightness: -100` copy of an image with `blur`, reduced `opacity` and a small offset, stacked beneath the real one, to fake the content-shaped drop shadow `shadow_x`/`shadow_y` cannot produce.
            - `contrast` number
            - `saturation` number
            - `exposure` number
            - `sharpness` number
            - `blur` number
            - `noise` number
            - `vignette` number
            - `hue_rotate` number
            - `temperature` number: White balance warm (+) / cool (−), −1..1; 0 = no-op. Applied as a channel matrix, after the classic brightness/contrast chain.
            - `tint` number: White balance magenta (+) / green (−), −1..1; 0 = no-op.
            - `vibrance` number: Saturation boost weighted toward muted colours, −1..1; 0 = no-op. Boosts flat colour without wrecking skin tones.
            - `highlights` number: Luma-masked tone lift of the brightest range, −1..1; 0 = no-op.
            - `shadows` number: Luma-masked tone lift of the darkest range, −1..1; 0 = no-op.
            - `crop_top` number
            - `crop_bottom` number
            - `crop_left` number
            - `crop_right` number
            - `border_radius` union: Corner radius in pixels — a single value, or per-corner `{top_left, top_right, bottom_right, bottom_left}` for asymmetric corners (e.g. a card rounded only on top). The renderer normalizes px to a 0–1 fraction of the element's shorter side (≈ half the shorter side is fully rounded) and clamps to that range. For shape elements use `corner_radius` (already 0–1).
              - …
            - `border_color` string, nullable
            - `border_width` union: Border thickness in pixels (converted to the renderer's relative border scale against the element's shorter side, so it survives resizes). The border outlines the element's border_radius-rounded quad — not the image's alpha silhouette.
              - …
            - `shadow_color` string, nullable
            - `shadow_blur` union: Shadow softness, 0–100 (renderer blur-strength scale, not pixels): 0 = hard edge, 100 = maximum blur. Values are clamped to that range.
              - …
            - `shadow_x` union: Horizontal shadow offset in pixels (converted to the renderer's relative offset scale). The shadow silhouette is the element's border_radius-rounded quad, not the content alpha — a cut-out PNG gets a card shadow, not an outline-shaped one. On a cut-out that reads as a floating rectangle (rotated with the element, which is the tell). For a shadow that follows the artwork, drop these fields and stack a second copy of the same image underneath at `brightness: -100` (a black silhouette, alpha preserved) with `blur` and reduced `opacity`, offset a few percent — see `brightness`.
              - …
            - `shadow_y` union: Vertical shadow offset in pixels. See `shadow_x`.
              - …
            - `blend_mode` 'none' | 'multiply' | 'screen' | 'overlay' | 'darken' | 'lighten' | 'color_dodge' | 'color_burn' | 'hard_light' | 'soft_light' | 'difference' | 'add' | 'exclusion' | 'hue' | 'saturation' | 'color' | 'luminosity': Values: - `none` — Normal compositing (source-over) — the default. - `multiply` — Multiply blend — darkens by multiplying layer colors. - `screen` — Screen blend — lightens by inverting, multiplying, inverting. - `overlay` — Overlay blend — multiply in shadows, screen in highlights. - `darken` — Darken blend — keeps the darker of the two layers per channel. - `lighten` — Lighten blend — keeps the lighter of the two layers per channel. - `color_dodge` — Color-dodge blend — brightens the base toward the blend color. - `color_burn` — Color-burn blend — darkens the base toward the blend color. - `hard_light` — Hard-light blend — overlay with layers swapped. - `soft_light` — Soft-light blend — a gentler hard-light. - `difference` — Difference blend — absolute difference of the two layers per channel. - `add` — Additive (linear-dodge) blend — sums layer colors, clamped. - `exclusion` — Exclusion blend — like difference, lower contrast. - `hue` — Hue blend — backdrop luminosity + saturation with the blend layer's hue. - `saturation` — Saturation blend — backdrop hue + luminosity with the blend layer's saturation. - `color` — Color blend — backdrop luminosity with the blend layer's hue + saturation. - `luminosity` — Luminosity blend — backdrop hue + saturation with the blend layer's luminosity.
            - `mask_shape` 'circle' | 'diamond' | 'hexagon' | 'star' | 'heart' | 'triangle': Analytic (SDF) shape mask that clips an element's coverage. §1. Values: - `circle` — Circular (ellipse) analytic mask, fit to the element bounds. - `diamond` — Diamond (rotated square) analytic mask. - `hexagon` — Regular hexagon analytic mask. - `star` — Five-point star analytic mask. - `heart` — Heart analytic mask. - `triangle` — Upward triangle analytic mask.
            - `mask` MaskConfig: Animated/feathered mask params layered on the analytic `mask_shape` (§5c).
              - …
            - `mask_keyframes` MaskKeyframe[], nullable
              - …
            - `matte` Matte: Track matte (§5d): drive this element's coverage from another layer's render.
              - …
            - `backdrop_blur` union: Glass: the scene behind the element, Gaussian-blurred, fills its footprint under its translucent content (a shape's own silhouette, a video's or image's box with its corner radius and mask). A Gaussian sigma in canvas px, as CSS `backdrop-filter: blur()` takes it; the renderer caps it at 128. 0 is off.
              - …
            - `backdrop_blur_keyframes` BlurKeyframe[], nullable: Animate `backdrop_blur` over the timeline; replaces the static value while present.
              - …
            - `layer_blur` number: Blurs the element itself as drawn: a Gaussian sigma in canvas px, as CSS `filter: blur()` takes it, spreading three sigma past its box. At most 128; 0 is off. Not on a shape yet (the editor's preview cannot draw it), and not with `blend_mode`, `backdrop_blur` or `matte`, beside which the renderer would draw the element without it.
            - `layer_blur_keyframes` BlurKeyframe[], nullable: Animate `layer_blur` over the timeline; replaces the static value while present.
              - …
            - `glass` Glass: A refracting glass surface (V6, render-node AEC §1): within `bevel` px of the element's edge (a shape's own outline, a box's rounded corners) the scene behind is read inward, bent by `refraction`, its red and blue split by `dispersion`. With `backdrop_blur` it bends the blurred scene, without it the sharp one.
              - …
            - `x` union
              - …
            - `y` union
              - …
            - `width` union: Box width. Together with `height` this is a **cover** box, not a fit box: sized media (video, image) scales to fill it and the overflow is **cropped** — it is never letterboxed. A box whose aspect ratio differs from the source's therefore silently cuts the source's edges. To show a source whole, match the box aspect to it: on a `W`x`H` canvas, for a source of aspect `a`, `width% = a * height% * (H/W)` — e.g. a 1024x1536 asset (a=0.667) on 1920x1080 needs `width% = 0.375 * height%`. Percentages are of the canvas, not the parent.
              - …
            - `height` union: Box height. See `width` — the pair is a cover box and crops rather than letterboxes. Percentages are of the canvas.
              - …
            - `aspect_ratio` number, nullable: **Accepted but ignored — the renderer never receives this field.** It does NOT constrain or correct the `width`/`height` box, so it cannot be used to stop a mismatched box from cropping the source; size the box per `width` instead. Kept for backward compatibility.
            - `x_anchor` union: Horizontal pivot the element **rotates** about, measured across its own box (`0%` = left edge, `50%` = centre, `100%` = right edge; px values are an offset from the left edge). This is a pivot, not an alignment — `x` always places the element's centre, so the anchor changes nothing on an unrotated element. Honored on video, image and shape; text and the generator elements rotate about their centre and reject a non-default anchor.
              - …
            - `y_anchor` union: Vertical pivot the element **rotates** about, measured across its own box (`0%` = top edge, `50%` = centre, `100%` = bottom edge). Same rules as `x_anchor`: a pivot, not an alignment.
              - …
            - `x_rotation` union
              - …
            - `y_rotation` union
              - …
            - `z_rotation` union
              - …
            - `x_scale` union
              - …
            - `y_scale` union
              - …
            - `flip_horizontal` boolean
            - `flip_vertical` boolean
            - `opacity` number
            - `z_index` integer, nullable: Stacking order (higher = on top). When omitted, elements stack in array order — later elements render above earlier ones (painter's rule); text defaults one layer above non-text. Set explicit values only when array order isn't the order you want.
            - `clip` boolean
            - `color_overlay` string, nullable: **Not supported by the renderer — setting this is rejected.** A strict submit returns `unsupported_feature` (422) and a dry-run reports it as a blocking violation; there is no native tint field to map it onto. To darken an element use `brightness` (`-100` is a black silhouette); to lay a colour over one, stack a `shape` with a flat or gradient `fill` above it.
            - `type` 'image'
            - `id` string, required: **Required.** Unique identifier for this image. An empty or missing ID causes the image to be silently skipped by the renderer.
            - `name` string, nullable: Optional human-readable label. Not used by the renderer.
            - `track` integer, nullable: Timeline track index (0–255). Informational only; not used by the renderer.
            - `time` number, nullable: When this element starts on the output timeline, in seconds. Defaults to `0` when omitted.
            - `visible` boolean: Set to `false` to skip this element without removing it from the request.
            - `locked` boolean: Marks the element locked in editors. Not sent to the renderer.
            - `duration` number, required: **Required.** Duration in seconds the image is visible. Omitting this field produces a zero-length window and the image will not appear in the render.
            - `source_url` string, uri, required: URL of the image file (PNG, JPG, WebP, GIF, SVG). A `.svg` draws a subset: groups and the basic shapes and paths, filled and stroked with flat colours. An element that uses anything else (a `style` attribute, `clip-path`, `mask`, `filter`, `stroke-dasharray`, a gradient or `currentColor` paint, `<text>`, `<use>`, `<image>`) is not drawn, nor anything inside it; an SVG that would draw nothing at all is refused at submit. Set explicit colours and presentation attributes, or pre-rasterize to PNG.
            - `effects` Effect[]
              - …
            - `motion` Motion[]
              - …
          - ShapeElement: A first-class vector primitive: SVG path fill + stroke, morphing (§5b).
            - `blend_mode` 'none' | 'multiply' | 'screen' | 'overlay' | 'darken' | 'lighten' | 'color_dodge' | 'color_burn' | 'hard_light' | 'soft_light' | 'difference' | 'add' | 'exclusion' | 'hue' | 'saturation' | 'color' | 'luminosity': Values: - `none` — Normal compositing (source-over) — the default. - `multiply` — Multiply blend — darkens by multiplying layer colors. - `screen` — Screen blend — lightens by inverting, multiplying, inverting. - `overlay` — Overlay blend — multiply in shadows, screen in highlights. - `darken` — Darken blend — keeps the darker of the two layers per channel. - `lighten` — Lighten blend — keeps the lighter of the two layers per channel. - `color_dodge` — Color-dodge blend — brightens the base toward the blend color. - `color_burn` — Color-burn blend — darkens the base toward the blend color. - `hard_light` — Hard-light blend — overlay with layers swapped. - `soft_light` — Soft-light blend — a gentler hard-light. - `difference` — Difference blend — absolute difference of the two layers per channel. - `add` — Additive (linear-dodge) blend — sums layer colors, clamped. - `exclusion` — Exclusion blend — like difference, lower contrast. - `hue` — Hue blend — backdrop luminosity + saturation with the blend layer's hue. - `saturation` — Saturation blend — backdrop hue + luminosity with the blend layer's saturation. - `color` — Color blend — backdrop luminosity with the blend layer's hue + saturation. - `luminosity` — Luminosity blend — backdrop hue + saturation with the blend layer's luminosity.
            - `mask_shape` 'circle' | 'diamond' | 'hexagon' | 'star' | 'heart' | 'triangle': Analytic (SDF) shape mask that clips an element's coverage. §1. Values: - `circle` — Circular (ellipse) analytic mask, fit to the element bounds. - `diamond` — Diamond (rotated square) analytic mask. - `hexagon` — Regular hexagon analytic mask. - `star` — Five-point star analytic mask. - `heart` — Heart analytic mask. - `triangle` — Upward triangle analytic mask.
            - `mask` MaskConfig: Animated/feathered mask params layered on the analytic `mask_shape` (§5c).
              - …
            - `mask_keyframes` MaskKeyframe[], nullable
              - …
            - `matte` Matte: Track matte (§5d): drive this element's coverage from another layer's render.
              - …
            - `backdrop_blur` union: Glass: the scene behind the element, Gaussian-blurred, fills its footprint under its translucent content (a shape's own silhouette, a video's or image's box with its corner radius and mask). A Gaussian sigma in canvas px, as CSS `backdrop-filter: blur()` takes it; the renderer caps it at 128. 0 is off.
              - …
            - `backdrop_blur_keyframes` BlurKeyframe[], nullable: Animate `backdrop_blur` over the timeline; replaces the static value while present.
              - …
            - `layer_blur` number: Blurs the element itself as drawn: a Gaussian sigma in canvas px, as CSS `filter: blur()` takes it, spreading three sigma past its box. At most 128; 0 is off. Not on a shape yet (the editor's preview cannot draw it), and not with `blend_mode`, `backdrop_blur` or `matte`, beside which the renderer would draw the element without it.
            - `layer_blur_keyframes` BlurKeyframe[], nullable: Animate `layer_blur` over the timeline; replaces the static value while present.
              - …
            - `glass` Glass: A refracting glass surface (V6, render-node AEC §1): within `bevel` px of the element's edge (a shape's own outline, a box's rounded corners) the scene behind is read inward, bent by `refraction`, its red and blue split by `dispersion`. With `backdrop_blur` it bends the blurred scene, without it the sharp one.
              - …
            - `x` union
              - …
            - `y` union
              - …
            - `width` union: Box width. Together with `height` this is a **cover** box, not a fit box: sized media (video, image) scales to fill it and the overflow is **cropped** — it is never letterboxed. A box whose aspect ratio differs from the source's therefore silently cuts the source's edges. To show a source whole, match the box aspect to it: on a `W`x`H` canvas, for a source of aspect `a`, `width% = a * height% * (H/W)` — e.g. a 1024x1536 asset (a=0.667) on 1920x1080 needs `width% = 0.375 * height%`. Percentages are of the canvas, not the parent.
              - …
            - `height` union: Box height. See `width` — the pair is a cover box and crops rather than letterboxes. Percentages are of the canvas.
              - …
            - `aspect_ratio` number, nullable: **Accepted but ignored — the renderer never receives this field.** It does NOT constrain or correct the `width`/`height` box, so it cannot be used to stop a mismatched box from cropping the source; size the box per `width` instead. Kept for backward compatibility.
            - `x_anchor` union: Horizontal pivot the element **rotates** about, measured across its own box (`0%` = left edge, `50%` = centre, `100%` = right edge; px values are an offset from the left edge). This is a pivot, not an alignment — `x` always places the element's centre, so the anchor changes nothing on an unrotated element. Honored on video, image and shape; text and the generator elements rotate about their centre and reject a non-default anchor.
              - …
            - `y_anchor` union: Vertical pivot the element **rotates** about, measured across its own box (`0%` = top edge, `50%` = centre, `100%` = bottom edge). Same rules as `x_anchor`: a pivot, not an alignment.
              - …
            - `x_rotation` union
              - …
            - `y_rotation` union
              - …
            - `z_rotation` union
              - …
            - `x_scale` union
              - …
            - `y_scale` union
              - …
            - `flip_horizontal` boolean
            - `flip_vertical` boolean
            - `opacity` number
            - `z_index` integer, nullable: Stacking order (higher = on top). When omitted, elements stack in array order — later elements render above earlier ones (painter's rule); text defaults one layer above non-text. Set explicit values only when array order isn't the order you want.
            - `clip` boolean
            - `color_overlay` string, nullable: **Not supported by the renderer — setting this is rejected.** A strict submit returns `unsupported_feature` (422) and a dry-run reports it as a blocking violation; there is no native tint field to map it onto. To darken an element use `brightness` (`-100` is a black silhouette); to lay a colour over one, stack a `shape` with a flat or gradient `fill` above it.
            - `type` 'shape'
            - `id` string, required: **Required.** Unique identifier.
            - `name` string, nullable: Optional human-readable label. Not used by the renderer.
            - `track` integer, nullable: Timeline track index (0–255). Informational only; not used by the renderer.
            - `time` number, nullable: When this element starts on the output timeline, in seconds. Defaults to `0` when omitted.
            - `visible` boolean: Set to `false` to skip this element without removing it from the request.
            - `locked` boolean: Marks the element locked in editors. Not sent to the renderer.
            - `duration` number, required: **Required.** Seconds the shape is visible.
            - `path` string, nullable: SVG path `d` (M L H V C S Q T A Z; elliptical arcs render as arcs). **Required** unless `geometry` is set.
            - `geometry` ShapeGeometry: A closed-form shape the engines draw analytically (AEC §5f): exactly one kind.
              - …
            - `view_box` number[]: `[x, y, width, height]` path coordinate space.
            - `fill` union: Fill: a hex colour (alpha ok), `"none"` for outline-only, a Gradient object (`{kind, angle_degrees, center, stops}`, ≥2 stops), or a CSS `linear-gradient(...)` / `radial-gradient(...)` string, which is normalised to the Gradient object at validation — read back, the composition holds the object. A gradient fill on a rectangular path is how you draw a gradient card, bar or pill — there is no separate `gradient` element. The gradient paints the shape's own path, so it fills any silhouette — a triangle, a blob, an icon outline, a morphing path — and layers on the same element with `stroke` / `stroke_width` / `stroke_dash`, `path_keyframes` (morph), `trim` / `trim_keyframes` (draw-on), `blend_mode`, `mask_shape` / `mask` / `mask_keyframes`, `matte` and `backdrop_blur`. `fill_rule`, `corner_radius` and `opacity` behave with a gradient exactly as they do with a flat colour.
              - …
            - `fill_rule` 'nonzero' | 'evenodd'
            - `stroke` string, nullable: Stroke colour.
            - `stroke_width` number, nullable: Stroke width in viewBox units, at most 100000 (absent = SVG 1.0; 0 disables).
            - `stroke_linecap` 'butt' | 'round' | 'square': Cap on the open ends of a stroked path (SVG `stroke-linecap`): `butt` stops at the endpoint, `round` and `square` extend it by half the stroke width. Closed paths have joins, not caps.
            - `stroke_linejoin` 'miter' | 'round' | 'bevel': Corner join of a stroked path (SVG `stroke-linejoin`): `miter` is the sharp corner (subject to `stroke_miter_limit`), `bevel` cuts it flat.
            - `stroke_miter_limit` number: SVG `stroke-miterlimit`: miter length over stroke width. A `miter` join sharper than this falls back to `bevel`.
            - `stroke_dash` number[], nullable: Dash pattern in viewBox units, SVG `stroke-dasharray` semantics: `[on, off, ...]` of any length — an odd-length array repeats so on/off alternate (`[4]` is 4 on, 4 off). Trim applies before dash, so a dashed path can draw itself on without the pattern re-flowing.
            - `stroke_dash_offset` number: Where the dash pattern starts along the path, in viewBox units (SVG `stroke-dashoffset`): positive shifts the pattern forward, negative backward.
            - `stroke_dash_offset_keyframes` DashOffsetKeyframe[], nullable: Animate the dash offset (timeline seconds) — marching ants: keyframe `offset` linearly by one dash period (the pattern's sum) per second. Needs a `stroke_dash`; while present it drives the offset in place of the static `stroke_dash_offset`.
              - …
            - `stroke_marker_start` 'none' | 'triangle' | 'open_chevron' | 'dot': A head at the start of each open subpath, in the stroke's colour (render-node V2.6): `triangle` is filled, its tip on the endpoint (the shaft shortens to meet it); `open_chevron` is an open head stroked like the path; `dot` is a disc. Placed after the trim, so a draw-on keeps its head at the visible end, and left out of the dash. A closed subpath takes none. Needs a `stroke`; path shapes only.
            - `stroke_marker_end` 'none' | 'triangle' | 'open_chevron' | 'dot': A head at the end of each open subpath; see `stroke_marker_start`.
            - `stroke_marker_size` number: Marker length in stroke widths, 1–32 (4 is four times `stroke_width`).
            - `corner_radius` union: Corner radius, normalized 0–1 — a single value or per-corner `{top_left, top_right, bottom_right, bottom_left}`.
              - …
            - `shadow` DropShadow: Drop shadow for shape elements, in the renderer's own units. ``distance`` and ``blur`` are the engine's unitless 0–100 strength scale, NOT pixels — a px-looking ``distance: 10`` is a fraction of what you meant on a small element and renders as nothing at all. The offset lands at ``distance × 0.0005 × the element's shorter side``; the reference scenes author 60–70 for both. For pixel-unit shadows use ``shadow_x``/``shadow_y`` on video and image elements. The layout lint reports a distance that renders under a pixel as ``SHADOW_BELOW_MIN_SIZE``.
              - …
            - `path_keyframes` PathKeyframe[], nullable: Morph the path between keyframes (timeline seconds).
              - …
            - `trim` TrimPath
              - …
            - `trim_keyframes` TrimKeyframe[], nullable: Animate the trim window (timeline seconds) — the draw-on: animate `end` 0→1 on a stroked path to draw a signature, underline or chart line. Absent `start`/`end` inherit the static `trim`.
              - …
            - `effects` Effect[]: Shader effect chain (§6 W3).
              - …
            - `motion` Motion[]
              - …
          - FrameElement: A frame (render-node AEC §5): elements nested in its box, with its own clip, mask, matte, blend, effects and, as a composite, its own clock. Its children use design px from its top-left corner, frames and instances included.
            - `x` number, nullable: Left edge in design px from its parent's left edge (at the root, the `design_size` box's, else the output's). Omitted: 0.
            - `y` number, nullable: Top edge in design px from its parent's top edge. Omitted: 0.
            - `x_anchor` union: Pivot it turns and scales about, across its own box: a percent, or design px from its left edge. Omitted: the centre.
              - …
            - `y_anchor` union: Pivot across its own box's height: a percent, or design px from its top edge. Omitted: the centre.
              - …
            - `z_rotation` union: Rotation in degrees. Omitted: 0.
              - …
            - `x_rotation` union: Perspective tilt (X axis) each element inside takes about its own centre, as a group's does. Omitted: 0.
              - …
            - `y_rotation` union: Perspective tilt (Y axis); see `x_rotation`. Omitted: 0.
              - …
            - `scale` number, nullable: Uniform scale about the anchor. Omitted: 1.
            - `opacity` number, nullable: Opacity 0 to 100 of everything inside. Omitted: 100.
            - `z_index` integer, nullable: Stacking order among its siblings (higher on top); ties draw in array order. Everything inside draws together at its place.
            - `duration` number, nullable: Seconds active. Omitted: until its parent ends.
            - `motion` Motion[]: Animations of the whole, about its anchor.
              - …
            - `clip` boolean, nullable: Crop what is inside to its box.
            - `corner_radius` union: Corner radius of the clip in design px: a number, `[top_left, top_right, bottom_right, bottom_left]` or `{top_left, ...}`. Applies with `clip`.
              - …
            - `blend_mode` 'none' | 'multiply' | 'screen' | 'overlay' | 'darken' | 'lighten' | 'color_dodge' | 'color_burn' | 'hard_light' | 'soft_light' | 'difference' | 'add' | 'exclusion' | 'hue' | 'saturation' | 'color' | 'luminosity': Values: - `none` — Normal compositing (source-over) — the default. - `multiply` — Multiply blend — darkens by multiplying layer colors. - `screen` — Screen blend — lightens by inverting, multiplying, inverting. - `overlay` — Overlay blend — multiply in shadows, screen in highlights. - `darken` — Darken blend — keeps the darker of the two layers per channel. - `lighten` — Lighten blend — keeps the lighter of the two layers per channel. - `color_dodge` — Color-dodge blend — brightens the base toward the blend color. - `color_burn` — Color-burn blend — darkens the base toward the blend color. - `hard_light` — Hard-light blend — overlay with layers swapped. - `soft_light` — Soft-light blend — a gentler hard-light. - `difference` — Difference blend — absolute difference of the two layers per channel. - `add` — Additive (linear-dodge) blend — sums layer colors, clamped. - `exclusion` — Exclusion blend — like difference, lower contrast. - `hue` — Hue blend — backdrop luminosity + saturation with the blend layer's hue. - `saturation` — Saturation blend — backdrop hue + luminosity with the blend layer's saturation. - `color` — Color blend — backdrop luminosity with the blend layer's hue + saturation. - `luminosity` — Luminosity blend — backdrop hue + saturation with the blend layer's luminosity.
            - `mask_shape` 'circle' | 'diamond' | 'hexagon' | 'star' | 'heart' | 'triangle': Analytic (SDF) shape mask that clips an element's coverage. §1. Values: - `circle` — Circular (ellipse) analytic mask, fit to the element bounds. - `diamond` — Diamond (rotated square) analytic mask. - `hexagon` — Regular hexagon analytic mask. - `star` — Five-point star analytic mask. - `heart` — Heart analytic mask. - `triangle` — Upward triangle analytic mask.
            - `mask` MaskConfig: Animated/feathered mask params layered on the analytic `mask_shape` (§5c).
              - …
            - `matte` Matte: Track matte (§5d): drive this element's coverage from another layer's render.
              - …
            - `effects` Effect[]: Shader effects run over its content.
              - …
            - `render` 'flatten' | 'composite', nullable: `composite` makes it a nested composition whose content runs on `time_map`'s clock; its content is graphics only (no video). Omitted: `flatten`.
            - `time_map` TimeMap: A composite frame's clock (AEC §5): the time its children read.
              - …
            - `type` 'frame'
            - `id` string, required: **Required.** Unique in the request; operations, mattes and anchors name it.
            - `name` string, nullable: Optional human-readable label. Not used by the renderer.
            - `track` integer, nullable: Timeline track index (0–255). Informational only; not used by the renderer.
            - `time` number, nullable: Start in seconds on its parent's clock (the composition's at the root). Omitted: its parent's start.
            - `visible` boolean: Set to `false` to skip this element without removing it from the request.
            - `locked` boolean: Marks the element locked in editors. Not sent to the renderer.
            - `width` number, nullable: Width in design px. Omitted: its parent's width.
            - `height` number, nullable: Height in design px. Omitted: its parent's height.
            - `slot` string, nullable: In a component body: the slot name an instance's `slots` fill in place of this frame's own children.
            - `children` union[]: The elements inside, in design px from this frame's top-left corner; frames and instances nest. Audio stays at the root.
              - …
          - InstanceElement: An instance (render-node AEC §5g): a placed copy of a `components` definition. Its box is the component's (or its variant's); it sets the component's properties, fills its slots, and places, times and composites itself as a frame does.
            - `x` number, nullable: Left edge in design px from its parent's left edge (at the root, the `design_size` box's, else the output's). Omitted: 0.
            - `y` number, nullable: Top edge in design px from its parent's top edge. Omitted: 0.
            - `x_anchor` union: Pivot it turns and scales about, across its own box: a percent, or design px from its left edge. Omitted: the centre.
              - …
            - `y_anchor` union: Pivot across its own box's height: a percent, or design px from its top edge. Omitted: the centre.
              - …
            - `z_rotation` union: Rotation in degrees. Omitted: 0.
              - …
            - `x_rotation` union: Perspective tilt (X axis) each element inside takes about its own centre, as a group's does. Omitted: 0.
              - …
            - `y_rotation` union: Perspective tilt (Y axis); see `x_rotation`. Omitted: 0.
              - …
            - `scale` number, nullable: Uniform scale about the anchor. Omitted: 1.
            - `opacity` number, nullable: Opacity 0 to 100 of everything inside. Omitted: 100.
            - `z_index` integer, nullable: Stacking order among its siblings (higher on top); ties draw in array order. Everything inside draws together at its place.
            - `duration` number, nullable: Seconds active. Omitted: until its parent ends.
            - `motion` Motion[]: Animations of the whole, about its anchor.
              - …
            - `clip` boolean, nullable: Crop what is inside to its box.
            - `corner_radius` union: Corner radius of the clip in design px: a number, `[top_left, top_right, bottom_right, bottom_left]` or `{top_left, ...}`. Applies with `clip`.
              - …
            - `blend_mode` 'none' | 'multiply' | 'screen' | 'overlay' | 'darken' | 'lighten' | 'color_dodge' | 'color_burn' | 'hard_light' | 'soft_light' | 'difference' | 'add' | 'exclusion' | 'hue' | 'saturation' | 'color' | 'luminosity': Values: - `none` — Normal compositing (source-over) — the default. - `multiply` — Multiply blend — darkens by multiplying layer colors. - `screen` — Screen blend — lightens by inverting, multiplying, inverting. - `overlay` — Overlay blend — multiply in shadows, screen in highlights. - `darken` — Darken blend — keeps the darker of the two layers per channel. - `lighten` — Lighten blend — keeps the lighter of the two layers per channel. - `color_dodge` — Color-dodge blend — brightens the base toward the blend color. - `color_burn` — Color-burn blend — darkens the base toward the blend color. - `hard_light` — Hard-light blend — overlay with layers swapped. - `soft_light` — Soft-light blend — a gentler hard-light. - `difference` — Difference blend — absolute difference of the two layers per channel. - `add` — Additive (linear-dodge) blend — sums layer colors, clamped. - `exclusion` — Exclusion blend — like difference, lower contrast. - `hue` — Hue blend — backdrop luminosity + saturation with the blend layer's hue. - `saturation` — Saturation blend — backdrop hue + luminosity with the blend layer's saturation. - `color` — Color blend — backdrop luminosity with the blend layer's hue + saturation. - `luminosity` — Luminosity blend — backdrop hue + saturation with the blend layer's luminosity.
            - `mask_shape` 'circle' | 'diamond' | 'hexagon' | 'star' | 'heart' | 'triangle': Analytic (SDF) shape mask that clips an element's coverage. §1. Values: - `circle` — Circular (ellipse) analytic mask, fit to the element bounds. - `diamond` — Diamond (rotated square) analytic mask. - `hexagon` — Regular hexagon analytic mask. - `star` — Five-point star analytic mask. - `heart` — Heart analytic mask. - `triangle` — Upward triangle analytic mask.
            - `mask` MaskConfig: Animated/feathered mask params layered on the analytic `mask_shape` (§5c).
              - …
            - `matte` Matte: Track matte (§5d): drive this element's coverage from another layer's render.
              - …
            - `effects` Effect[]: Shader effects run over its content.
              - …
            - `render` 'flatten' | 'composite', nullable: `composite` makes it a nested composition whose content runs on `time_map`'s clock; its content is graphics only (no video). Omitted: `flatten`.
            - `time_map` TimeMap: A composite frame's clock (AEC §5): the time its children read.
              - …
            - `type` 'instance'
            - `id` string, required: **Required.** Its content's ids become `<id>/<child id>`.
            - `name` string, nullable: Optional human-readable label. Not used by the renderer.
            - `track` integer, nullable: Timeline track index (0–255). Informational only; not used by the renderer.
            - `time` number, required: **Required.** Start in seconds on its parent's clock; every time inside the component counts from it.
            - `visible` boolean: Set to `false` to skip this element without removing it from the request.
            - `locked` boolean: Marks the element locked in editors. Not sent to the renderer.
            - `component` string, required: The `components` entry it places.
            - `variant` string, nullable: The variant by name. Omitted: the first.
            - `properties` object: Values of the component's declared properties, by name, each of its type.
            - `slots` object: Content for the component's slots, by slot name, in place of the slot frame's own children.
      - `transitions` Transition[]: Transition effects applied between consecutive elements.
        - `type` 'cross_dissolve' | 'dip_to_black' | 'dip_to_white' | 'wipe_left' | 'wipe_right' | 'wipe_up' | 'wipe_down' | 'diagonal_wipe' | 'barn_doors_horizontal' | 'barn_doors_vertical' | 'iris' | 'page_turn' | 'cross_zoom' | 'gradient_wipe' | 'band_wipe' | 'box_wipe' | 'swirl' | 'glitch_memories' | 'window_slice' | 'cube' | 'doorway' | 'pinwheel' | 'water_drop' | 'crosshatch' | 'dreamy' | 'angular' | 'burn' | 'heart' | 'circle_open' | 'color_phase' | 'squares_wire' | 'whip_pan' | 'cinematic_zoom' | 'burn_edge' | 'shatter' | 'domain_warp' | 'circle_crop' | 'cross_warp' | 'fold_horizontal' | 'fold_vertical' | 'linear_blur' | 'minimise_bottom_left' | 'minimise_bottom_right' | 'minimise_top_left' | 'minimise_top_right' | 'ripple' | 'rotate' | 'splice' | 'three_stripes', required: Values: - `cross_dissolve` — Cross-dissolve — fades one clip into the next. - `dip_to_black` — Dips through black between clips. - `dip_to_white` — Dips through white between clips. - `wipe_left` — Wipes the next clip in from the right to the left. - `wipe_right` — Wipes the next clip in from the left to the right. - `wipe_up` — Wipes the next clip in from the bottom upward. - `wipe_down` — Wipes the next clip in from the top downward. - `diagonal_wipe` — Wipes across on a diagonal split. - `barn_doors_horizontal` — Two halves part horizontally like barn doors. - `barn_doors_vertical` — Two halves part vertically like barn doors. - `iris` — Circular iris (bullseye) open/close between clips. - `page_turn` — Page-flip / page-curl reveal. - `cross_zoom` — Zoom-blur push from one clip into the next. - `gradient_wipe` — Soft gradient-driven dissolve wipe. - `band_wipe` — Interleaved stripes wipe the next clip in. - `box_wipe` — Rectangular box grows to reveal the next clip. - `swirl` — Swirls the outgoing clip away in a spiral. - `glitch_memories` — Glitchy, memory-like distortion between clips. - `window_slice` — Slices the frame into sliding window strips. - `cube` — 3D cube rotation between the two clips. - `doorway` — Clips part like opening doors to reveal the next. - `pinwheel` — Pinwheel rotation wipe between clips. - `water_drop` — Rippling water-drop distortion reveals the next clip. - `crosshatch` — Crosshatch pattern dissolve between clips. - `dreamy` — Soft, dreamy blur-dissolve between clips. - `angular` — Angular wedge sweep between clips. - `burn` — Burning-film dissolve between clips. - `heart` — Heart-shaped iris reveal of the next clip. - `circle_open` — Expanding circle opens onto the next clip. - `color_phase` — Color-phase shift blends between clips. - `squares_wire` — Wireframe squares wipe between clips. - `whip_pan` — Directional motion-blur whip pan. - `cinematic_zoom` — Punch zoom with radial blur + chromatic fringing. - `burn_edge` — Noise-ridged burn front with ember glow and sparks. - `shatter` — Jittered shards fly apart revealing the incoming clip. - `domain_warp` — Organic noise-warped dissolve with an emissive frontier. - `circle_crop` — Both clips crop to a shrinking/growing circle through the cut. - `cross_warp` — Clips warp into each other along a dissolving threshold. - `fold_horizontal` — Outgoing clip folds away horizontally, revealing the incoming. - `fold_vertical` — Outgoing clip folds away vertically, revealing the incoming. - `linear_blur` — Cross-dissolve through a heavy directional blur peak. - `minimise_bottom_left` — Outgoing clip shrinks away into the bottom-left corner. - `minimise_bottom_right` — Outgoing clip shrinks away into the bottom-right corner. - `minimise_top_left` — Outgoing clip shrinks away into the top-left corner. - `minimise_top_right` — Outgoing clip shrinks away into the top-right corner. - `ripple` — Water-ripple distortion sweeps the cut across the frame. - `rotate` — Both clips rotate about the frame centre through the cut. - `splice` — Angled splice slides the incoming clip over the outgoing. - `three_stripes` — Three offset stripes sweep the incoming clip in.
        - `duration` number, required: Transition duration in seconds.
        - `from_id` string, nullable: ID of the outgoing video element (omit for the very first transition).
        - `to_id` string, nullable: ID of the incoming video element (omit for the very last transition).
        - `z_index` integer, nullable: **Deprecated — ignored by the renderer.** The transition draws at its linked videos' position; the engine reads no z key on a transition. Accepted for backward compatibility only.
      - `groups` Group[]: Transform groups over member elements (compose member→group→parent; optional stack layout), the alias of frames (§5). A request with `frame` or `instance` elements carries none.
        - `id` string, required: **Required.** Unique group id.
        - `parent` string, nullable: Parent group id for nesting.
        - `pivot` Point
          - `x` number
          - `y` number
        - `translate` Point
          - `x` number
          - `y` number
        - `scale` number: Group scale.
        - `rotation_degrees` number: In-plane (z) rotation of the group.
        - `x_rotation` union: Perspective tilt (X axis) applied to each member's orientation about its own centre — the tilted card-gallery look.
          - integer
          - number
          - string
        - `y_rotation` union: Perspective tilt (Y axis). See `x_rotation`.
          - integer
          - number
          - string
        - `opacity` number: Group opacity.
        - `members` string[]: Member element uuids.
        - `animations` Motion[]: Animations driving the whole group about `pivot`. Translate, scale, rotation and colour all compose — that is how you move or resize a whole scene as one unit. Two channels do NOT: a preset that animates **clip** (`wipe_left/right/up/down`) or **crop** (`ken_burns_*`) is a silent no-op on a group, doing nothing at all rather than erroring — put those on a member element instead. Non-uniform scale is also flattened here: only the x component of a `custom_animations` `scale` keyframe is used, so `{x:2,y:1}` scales uniformly. Note `zoom_out` barely moves (scale 1.0->0.9 under a full fade), so it reads as a pure fade; for a real shrink use a `custom` entry with the scale travel you want, or the group's static `scale`.
          - `type` 'identity' | 'fade' | 'slide_up' | 'slide_down' | 'slide_left' | 'slide_right' | 'zoom_in' | 'zoom_out' | 'rotate_cw' | 'rotate_ccw' | 'bounce' | 'wipe_left' | 'wipe_right' | 'wipe_up' | 'wipe_down' | 'ken_burns_in' | 'ken_burns_out' | 'ken_burns_in_out' | 'loop_wiggle' | 'loop_rotate' | 'loop_rotate_smooth' | 'loop_3d_spin' | 'loop_3d_sway' | 'blur' | 'evaporate' | 'overlay' | 'difference' | 'rubber_in' | 'whip_up' | 'whip_down' | 'glitch_pop' | 'drift_in' | 'drift_out' | 'loop_breathe' | 'loop_shimmer' | 'swing_in' | 'swing_out' | 'elastic_rise' | 'elastic_drop' | 'tilt_zoom' | 'loop_orbit' | 'smooth_pop': Values: - `identity` — Deliberate no-op carrier (no visible motion): attach a pure `audio` generator to any element without animating it. Element surfaces only. - `fade` — Fade in (entrance) or out (exit) via opacity. - `slide_up` — Slide in from below / out upward. - `slide_down` — Slide in from above / out downward. - `slide_left` — Slide in from the right / out to the left. - `slide_right` — Slide in from the left / out to the right. - `zoom_in` — Scale up from small to full size (entrance). - `zoom_out` — Scale down to small (exit). - `rotate_cw` — Spin in/out clockwise. - `rotate_ccw` — Spin in/out counter-clockwise. - `bounce` — Bounce in with an elastic overshoot / bounce out. - `wipe_left` — Reveal/conceal with a left-moving wipe. - `wipe_right` — Reveal/conceal with a right-moving wipe. - `wipe_up` — Reveal/conceal with an upward wipe. - `wipe_down` — Reveal/conceal with a downward wipe. - `ken_burns_in` — Slow zoom-in pan (Ken Burns) — entrance only. - `ken_burns_out` — Slow zoom-out pan (Ken Burns) — entrance only. - `ken_burns_in_out` — Slow zoom in then out (Ken Burns) — entrance only. - `loop_wiggle` — Continuous subtle wiggle (looping). - `loop_rotate` — Continuous stepped rotation (looping). - `loop_rotate_smooth` — Continuous smooth rotation (looping). - `loop_3d_spin` — Continuous 3D spin (looping). - `loop_3d_sway` — Continuous 3D sway (looping). - `blur` — Per-glyph soft blur-in (text entrance). - `evaporate` — Per-glyph rise, blur, and fade-out (text exit only). - `overlay` — Static Overlay blend fill (timing ignored). - `difference` — Static Difference blend fill (timing ignored). - `rubber_in` — Per-glyph rubbery overshoot as text springs in (text entrance). - `whip_up` — Per-glyph whip upward into place (text entrance). - `whip_down` — Per-glyph whip downward out of frame (text exit only). - `glitch_pop` — Per-glyph glitchy pop-in with jitter (text entrance). - `drift_in` — Per-glyph gentle drift and fade in (text entrance). - `drift_out` — Per-glyph gentle drift and fade out (text exit only). - `loop_breathe` — Per-glyph continuous breathing scale loop (text). - `loop_shimmer` — Per-glyph continuous shimmer loop (text). - `swing_in` — Element swings into place from an angle (entrance). - `swing_out` — Element swings away out of frame (exit only). - `elastic_rise` — Element rises with an elastic overshoot (entrance). - `elastic_drop` — Element drops away with an elastic recoil (exit only). - `tilt_zoom` — Element tilts and zooms into place (entrance). - `loop_orbit` — Element orbits continuously around its center (loop). - `smooth_pop` — Element pops in from 60% scale, overshooting slightly past full size before it settles, as it fades in (entrance).
          - `preset` string, nullable: Renderer animation name, used verbatim — any element animation on video/image/shape/group (e.g. `'pop'`, `'floatUp'`, `'out-slideBounceLeft'`, `'loop-heartBeat'`), any text animation on text (e.g. `'typewriter'`, `'roll'`, `'out-sink'`, `'loop-wavey'`). Reaches the presets `type` has no name for. The name carries its own direction: `out-*` names are exits, `loop-*` names loop with `duration` as one cycle, so `reversed` and `scope` do not apply. Exactly one of `type`, `preset` or `custom` must be set.
          - `custom` string, nullable: Name of a `custom_animations` entry to run instead of a catalog preset — the `element` registry for video/image/shape/group animations, the `text` registry for text. Exactly one of `type`, `preset` or `custom` must be set.
          - `time` number, required: Absolute start time on the output timeline in seconds. For entrances and loops set this to the element's `time`. For exits set this to element `time + duration − motion duration`. An entrance timed later than the element's own `time` leaves it fully rendered on screen until the entrance fires; the linter reports that as `ENTRANCE_AFTER_ELEMENT_START`.
          - `duration` number, required: How long the animation runs in seconds.
          - `easing` 'linear' | 'hold' | 'ease_in' | 'ease_out' | 'ease_in_out' | 'sin_in' | 'sin_out' | 'sin_in_out' | 'square_in' | 'square_out' | 'square_in_out' | 'expo_in' | 'expo_out' | 'expo_in_out' | 'quad_in' | 'quad_out' | 'quad_in_out' | 'cubic_in' | 'cubic_out' | 'cubic_in_out' | 'quart_in' | 'quart_out' | 'quart_in_out' | 'quint_in' | 'quint_out' | 'quint_in_out': The engine's shared easing name map (Easing.cpp `StringToEasingType`). Every name here resolves on every keyframe surface (morph/trim/mask/custom animation keyframes, selector ``ease``, counter ``easing``). ``ease_in`` / ``ease_out`` / ``ease_in_out`` are the AE/CSS aliases of the ``quad_*`` curves. ``square_*`` is a **step** family (``square_in_out`` snaps at the midpoint), not a power curve — the quadratic curves are ``quad_*``. ``hold`` freezes the prior value until the next keyframe. The parameterized families (spring / back / elastic / bezier) are not names: express them through the dedicated keyframe sub-objects. Values: - `linear` — Constant speed, no acceleration. - `hold` — Freeze the previous value until the next keyframe (step). - `ease_in` — Starts slow, accelerates (alias of quad_in). - `ease_out` — Starts fast, decelerates (alias of quad_out). - `ease_in_out` — Slow start and end, faster in the middle (alias of quad_in_out). - `sin_in` — Gentle sinusoidal acceleration in. - `sin_out` — Gentle sinusoidal deceleration out. - `sin_in_out` — Gentle sinusoidal ease both ends. - `square_in` — Step: snaps at the segment end (not a power curve). - `square_out` — Step: snaps at the segment start (not a power curve). - `square_in_out` — Step: snaps at the midpoint — the typewriter/glitch snap. - `expo_in` — Extreme exponential acceleration in. - `expo_out` — Extreme exponential deceleration out. - `expo_in_out` — Extreme exponential ease both ends. - `quad_in` — Quadratic acceleration in. - `quad_out` — Quadratic deceleration out. - `quad_in_out` — Quadratic ease both ends. - `cubic_in` — Cubic acceleration in (stronger than quad). - `cubic_out` — Cubic deceleration out (stronger than quad). - `cubic_in_out` — Cubic ease both ends. - `quart_in` — Quartic acceleration in. - `quart_out` — Quartic deceleration out. - `quart_in_out` — Quartic ease both ends. - `quint_in` — Quintic acceleration in (near-expo). - `quint_out` — Quintic deceleration out (near-expo). - `quint_in_out` — Quintic ease both ends.
          - `reversed` boolean: Run the preset's exit form instead of its entrance. Only presets that ship both forms accept it — `fade`, `slide_*`, `wipe_*`, `rotate_*`, `bounce`. Entrance-only presets have a separate exit-only type instead: use `zoom_out` (not `zoom_in` reversed), `evaporate` (not `blur`), `whip_down`, `drift_out`, `swing_out`, `elastic_drop` — those six set this flag for you. See `GET /v1/capabilities` `motion[].element_exit` for the per-preset truth.
          - `scope` 'element' | 'character': Whether a motion preset applies to the whole element or per-glyph (text only). Values: - `element` — The motion animates the whole element as one unit. - `character` — The motion animates each text glyph individually (text only).
          - `delay` number: Seconds to wait before the animation starts (folded into the animation's start time on the wire — the engine has no standalone pre-start delay). On loop presets it spaces the repeats instead.
          - `spacing` number, nullable: Stagger multiplier between per-glyph/word windows on text animations (engine default 1 = fully sequential; smaller values overlap the windows). Ignored by block animations.
          - `loop` boolean
          - `audio` AudioGenerator: Audio-reactive property offsets driven by the soundtrack FFT (§3, W2). Each frame the mix reduces to the chosen feature and the element's properties get offset by ``amount × feature`` — beat-bounce, bass-pumped scale, etc.
            - `feature` 'amplitude' | 'bass' | 'mid' | 'high' | 'beat'
            - `scale` number: Scale offset per unit feature.
            - `opacity` number: Opacity offset per unit feature.
            - `translate` Point
              - …
            - `rotate` number: Rotation offset (degrees) per unit feature.
        - `layout` StackLayout: Resolve members into a row/column stack (§5, A2): the groups alias's stack, placed as before. A `frame` has no layout of its own (the host's, render-node O27).
          - `type` 'stack'
          - `direction` 'row' | 'column'
          - `gap` number: Gap between members, normalized along the main axis.
          - `align` 'start' | 'center' | 'end'
          - `origin` Point
            - `x` number
            - `y` number
      - `design_size` DesignSize: The logical root (render-node AEC §6): the box frames and instances at the root are placed in, in design px, scaled uniformly to the output, whose aspect it must have.
        - `width` number, required: Width in design px.
        - `height` number, required: Height in design px.
      - `operations` OperationsBlock: The `operations` block (render-node AEC §3; GVR §4.3): element properties driven over time and evaluated by the engine every frame. Each group counts from its anchor.
        - `markers` OperationMarker[]: Named times `marker` anchors read.
          - `name` string, required
          - `time` number, required: Seconds on the composition clock.
        - `groups` OperationGroup[]: At most 1000 groups and 10000 operations.
          - `id` string, nullable: A stable id for editors.
          - `name` string, nullable: A label, e.g. `Animate in`.
          - `role` string, nullable: What the group is for in editors (`in`, `out`, `loop`, …). Not sent to the renderer.
          - `anchor` OperationAnchor: Where a group's times count from (GVR §4.3 item 8).
            - `ref` 'composition' | 'element.start' | 'element.end' | 'marker' | 'instance.start', required: `composition` (0 s), `element.start` or `element.end` of `target`, a marker by `name`, or `instance.start` of `target` (in a component, its own instance).
            - `target` string, nullable: The element or instance an element or instance anchor reads.
            - `name` string, nullable: The marker a `marker` anchor reads.
            - `offset` number, nullable: Seconds added to the anchor. Omitted: 0.
          - `ops` union[], required: Operations and presets in document order: at equal starts the later replace wins.
            - union
              - …
      - `components` Component[]: Component definitions, which `instance` elements place. Schema of one: `blocks.components` in GET /v1/capabilities.
        - `id` string, required: What an instance's `component` names.
        - `width` number, required: Box width in design px.
        - `height` number, required: Box height in design px.
        - `clip` boolean, nullable: Its instances clip to the box unless they say otherwise.
        - `corner_radius` union: Its instances' clip corner radius in design px.
          - number
          - number[]
          - GeometryCornerRadii: Per-corner radii in px; a corner left out is 0.
            - `top_left` number, nullable
            - `top_right` number, nullable
            - `bottom_right` number, nullable
            - `bottom_left` number, nullable
        - `properties` ComponentProperty[]: What an instance can set.
          - `name` string, required: A letter, then letters, digits or `_`.
          - `type` 'text' | 'colour' | 'number' | 'boolean' | 'media' | 'component' | 'variant', required: `text`, `colour` (`#RRGGBB` or `#RRGGBBAA`), `number`, `boolean`, `media` (a URL), `component` (an id from `allowed`) or `variant` (a nested instance's).
          - `bind` string[], required: Body fields it writes, as `<child id>.<field>` in this API's field names: any element's `x`, `y`, `z_rotation`, `opacity` and `visible`; a frame's or instance's `scale` and `clip`; a text's `text`, `text_color` and `font_size`; a shape's `fill`, `stroke` and `stroke_width`; an image's or video's `source_url`; an instance's `component`, `variant` and `properties.<name>`.
          - `default` union: Its value when an instance sets none. Omitted: the body's own.
            - boolean
            - integer
            - number
            - string
          - `min` number, nullable: A `number` property's lowest value.
          - `max` number, nullable: A `number` property's highest value.
          - `allowed` string[], nullable: The component ids a `component` property may swap in. Required on one.
        - `children` union[], nullable: The body of a single-variant component, in design px from the box's top-left corner. Give this or `variants`.
          - union
            - ChildVideoElement: A video inside a frame. Not inside a composite frame, whose content is graphics only.
              - …
            - ChildTextElement: A text inside a frame; `width` is its wrap width.
              - …
            - ChildImageElement: An image inside a frame.
              - …
            - ChildShapeElement: A shape inside a frame.
              - …
            - FrameElement: A frame (render-node AEC §5): elements nested in its box, with its own clip, mask, matte, blend, effects and, as a composite, its own clock. Its children use design px from its top-left corner, frames and instances included.
              - …
            - InstanceElement: An instance (render-node AEC §5g): a placed copy of a `components` definition. Its box is the component's (or its variant's); it sets the component's properties, fills its slots, and places, times and composites itself as a frame does.
              - …
        - `operations` OperationsBlock: The `operations` block (render-node AEC §3; GVR §4.3): element properties driven over time and evaluated by the engine every frame. Each group counts from its anchor.
          - `markers` OperationMarker[]: Named times `marker` anchors read.
            - `name` string, required
            - `time` number, required: Seconds on the composition clock.
          - `groups` OperationGroup[]: At most 1000 groups and 10000 operations.
            - `id` string, nullable: A stable id for editors.
            - `name` string, nullable: A label, e.g. `Animate in`.
            - `role` string, nullable: What the group is for in editors (`in`, `out`, `loop`, …). Not sent to the renderer.
            - `anchor` OperationAnchor: Where a group's times count from (GVR §4.3 item 8).
              - …
            - `ops` union[], required: Operations and presets in document order: at equal starts the later replace wins.
              - …
        - `variants` ComponentVariant[], nullable: Named bodies, the first the default. Give this or `children`.
          - `name` string, required
          - `width` number, nullable: Its box width in design px. Omitted: the component's.
          - `height` number, nullable: Its box height in design px. Omitted: the component's.
          - `children` union[]: Its body, in design px from the box's top-left corner.
            - union
              - …
          - `operations` OperationsBlock: The `operations` block (render-node AEC §3; GVR §4.3): element properties driven over time and evaluated by the engine every frame. Each group counts from its anchor.
            - `markers` OperationMarker[]: Named times `marker` anchors read.
              - …
            - `groups` OperationGroup[]: At most 1000 groups and 10000 operations.
              - …
      - `motion_blur` MotionBlur: Shutter-based temporal supersampling of the whole composition (§5c). Opt-in and costly: every frame renders `samples` times, and accumulation is CPU-side, so the render falls back to **software encoding** — expect roughly `samples`× render time.
        - `samples` integer, required: Temporal samples per frame (2–32).
        - `shutter` number: Shutter fraction of the frame interval, (0–1]. (Exactly 0 is rejected: it renders N identical samples — full cost, zero blur.)
      - `background_gradient` union: Canvas-sized gradient fill behind every layer (§6). Rendered for the full output slice. Accepts a Gradient object or a CSS `linear-gradient(...)` string.
        - string: CSS gradient shorthand — `linear-gradient(...)` or `radial-gradient(...)`. Normalised to the Gradient object during validation; the renderer never sees a CSS string, and reading the composition back returns the object.
        - Gradient
          - `kind` 'linear' | 'radial' | 'conic': `conic` sweeps the stops clockwise once round `center`, starting at `angle_degrees`.
          - `angle_degrees` number: Linear gradient angle in degrees, measured clockwise on the screen (y down): 0 = left→right (offset 0 at the left), 90 = top→bottom, 45 = top-left→bottom-right, 315 = bottom-left→top-right, and the same convention governs a shape's gradient `fill`. A numeric CSS angle is box-relative: `135deg` behaves as `to bottom right` whatever the card's shape; only 0/90/180/270 are pixel-exact. On a conic gradient: where the sweep starts, degrees clockwise from 3 o'clock.
          - `center` Point
            - `x` number
            - `y` number
          - `stops` GradientStop[], required: Two or more colour stops.
            - `offset` number, required: Position along the gradient, 0–1.
            - `color` string, required: Stop colour (hex, alpha supported).
      - `watermark_url` string, uri, nullable: Watermark image overlaid on the output (§6).
      - `custom_animations` CustomAnimationRegistry: Inline custom-animation registries for element and text animations (§3). `element` entries are bindable from any video/image/shape/group animation via `motion[].custom` (or `animations[].custom` on groups). `text` entries bind from a text element's `animation_preset` or `motion[].custom`. Names that shadow a catalog preset — or the reserved static keys `none`/ `overlay`/`difference` — are rejected here: the engine warn-and-*ignores* such a definition (renderer_node.cpp:1500-1516) and the reference then silently resolves to the catalog preset instead of yours.
        - `element` CustomAnimation[]
          - `name` string, required: Reference name (reserved/catalog-shadowing names are rejected).
          - `keyframes` AnimationKeyframe[], required
            - `time` number, required: Fraction of the animation (0–1).
            - `easing` string, nullable: Easing into this keyframe — any `easings` catalog name (`quad_out`, `expo_in_out`, `hold`, …). Unknown names are rejected. A `bezier`/`spring`/`back`/`elastic` sub-object overrides it.
            - `bezier` BezierEasing: CSS cubic-bezier(x1, y1, x2, y2) timing curve.
              - …
            - `spring` SpringEasing: Damped-spring ease over the keyframe segment. Only the damping ratio zeta = damping / (2 * sqrt(stiffness * mass)) shapes the curve; the segment's duration sets its timing. Below 1 the spring overshoots and rings (the defaults give zeta = 0.5); at 1 or above it settles without overshoot. An underdamped spring runs until its envelope decays to 0.1% (settle x w0 = ln(1000) / zeta, w0 = sqrt(stiffness / mass)). Since render-node's overdamped settle fix (GVR M3), a spring at zeta >= 1 runs over its slowest decay rate instead; before it, an overdamped spring's window was cut short and it reached the target still moving.
              - …
            - `back` BackEasing: Anticipate/overshoot ease (AE 'back').
              - …
            - `elastic` ElasticEasing: Elastic ring-out/ring-in ease.
              - …
            - `translate` Point
              - …
            - `path` SpatialPath: Cubic-bezier control points curving the translate into this keyframe. The segment from the previous keyframe's position travels along the bezier defined by `c1`/`c2` (same units as `translate`) instead of a straight line.
              - …
            - `scale` union: Uniform scale factor, or `{x, y}` for non-uniform scale.
              - …
            - `rotate` number, nullable: In-plane (z) rotation, degrees.
            - `rotation` RotationXYZ: 3D rotation (degrees) for a custom-animation keyframe. Non-zero `x`/`y` renders true perspective tilt through the engine's 45° fov camera — the animated 3D-card family (spins, tilting reveals). This is also the engine-recommended way to tilt *text*, which has no static tilt.
              - …
            - `opacity` number, nullable
            - `color` string, nullable: Colour tint at this keyframe (multiplied with the element). An 8-digit hex alpha composes with `opacity` — when both are set, `opacity` wins for the alpha channel.
            - `blur` number, nullable: Per-glyph blur amount (fraction of the em). Text registries only.
          - `group` 'block' | 'word' | 'glyph': What the animation repeats over: the whole text block, each word, or each glyph. Required for `selector` to shape anything — a selector over a single block has nothing to range across. Text registries only.
          - `selector` RangeSelector: AE-style range selector: shape a text animation's amount by position (§4, W2).
            - `based_on` 'glyph' | 'word'
            - `start` number: Range start, fraction along the text.
            - `end` number: Range end, fraction along the text.
            - `falloff` number: Ease to zero over this fraction beyond each edge.
            - `ease` string, nullable: Easing curve name for the falloff.
            - `invert` boolean
        - `text` CustomAnimation[]
          - `name` string, required: Reference name (reserved/catalog-shadowing names are rejected).
          - `keyframes` AnimationKeyframe[], required
            - `time` number, required: Fraction of the animation (0–1).
            - `easing` string, nullable: Easing into this keyframe — any `easings` catalog name (`quad_out`, `expo_in_out`, `hold`, …). Unknown names are rejected. A `bezier`/`spring`/`back`/`elastic` sub-object overrides it.
            - `bezier` BezierEasing: CSS cubic-bezier(x1, y1, x2, y2) timing curve.
              - …
            - `spring` SpringEasing: Damped-spring ease over the keyframe segment. Only the damping ratio zeta = damping / (2 * sqrt(stiffness * mass)) shapes the curve; the segment's duration sets its timing. Below 1 the spring overshoots and rings (the defaults give zeta = 0.5); at 1 or above it settles without overshoot. An underdamped spring runs until its envelope decays to 0.1% (settle x w0 = ln(1000) / zeta, w0 = sqrt(stiffness / mass)). Since render-node's overdamped settle fix (GVR M3), a spring at zeta >= 1 runs over its slowest decay rate instead; before it, an overdamped spring's window was cut short and it reached the target still moving.
              - …
            - `back` BackEasing: Anticipate/overshoot ease (AE 'back').
              - …
            - `elastic` ElasticEasing: Elastic ring-out/ring-in ease.
              - …
            - `translate` Point
              - …
            - `path` SpatialPath: Cubic-bezier control points curving the translate into this keyframe. The segment from the previous keyframe's position travels along the bezier defined by `c1`/`c2` (same units as `translate`) instead of a straight line.
              - …
            - `scale` union: Uniform scale factor, or `{x, y}` for non-uniform scale.
              - …
            - `rotate` number, nullable: In-plane (z) rotation, degrees.
            - `rotation` RotationXYZ: 3D rotation (degrees) for a custom-animation keyframe. Non-zero `x`/`y` renders true perspective tilt through the engine's 45° fov camera — the animated 3D-card family (spins, tilting reveals). This is also the engine-recommended way to tilt *text*, which has no static tilt.
              - …
            - `opacity` number, nullable
            - `color` string, nullable: Colour tint at this keyframe (multiplied with the element). An 8-digit hex alpha composes with `opacity` — when both are set, `opacity` wins for the alpha channel.
            - `blur` number, nullable: Per-glyph blur amount (fraction of the em). Text registries only.
          - `group` 'block' | 'word' | 'glyph': What the animation repeats over: the whole text block, each word, or each glyph. Required for `selector` to shape anything — a selector over a single block has nothing to range across. Text registries only.
          - `selector` RangeSelector: AE-style range selector: shape a text animation's amount by position (§4, W2).
            - `based_on` 'glyph' | 'word'
            - `start` number: Range start, fraction along the text.
            - `end` number: Range end, fraction along the text.
            - `falloff` number: Ease to zero over this fraction beyond each edge.
            - `ease` string, nullable: Easing curve name for the falloff.
            - `invert` boolean
      - `metadata` object: Arbitrary key-value pairs echoed back in all webhook payloads.
      - `webhook_url` string, uri, nullable: Per-request webhook URL. Overrides workspace-level webhooks for this job only. Receives `render.completed`, `render.failed`, and progress events.
      - `ingest_external` boolean, nullable: Controls handling of video/audio source URLs that are not hosted by Framelane. When `true` (the default), each such file is probed for its duration before rendering: the job starts in the `ingesting` state and moves to `queued` once the probe answers, so elements may omit `out_point`. The file itself is fetched by the renderer at render time, so the URL must be a direct, public https link to the media (not a share page) that supports range requests. When `false`, the URL is passed straight through and video/audio require an explicit `out_point`.
  - `next_cursor` string, nullable
  - `has_more` boolean

## Other responses

- `401`: Missing or invalid API key.
- `422`: Validation Error

---

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