Every change Wellknown observed on this MCP server, newest first, with what it was before and what it became. Tool-surface changes carry the definition diff. Nothing here is edited after the fact.
Authorization not required, issuer https://auth.clueso.io/oauth/2.1
Changed the definition of "get_recording_activity", "record_screen" and "set_voice"
⟨47 unchanged words⟩ one project with its latest recording's `state` (pending: waiting for a free recording slot and starts by itself; in_progress, done or failed), the `recorders` used ⟨99 unchanged words⟩
⟨35 unchanged words⟩ rows in this state.","enum":["pending","in_progress","done","failed"],"type": ⟨3 unchanged words⟩
⟨967 unchanged words⟩ near_exact","exact"],"type":"string"},"voice_version":{"description":"ElevenLabs voice generation for every scene's voice. Leave unset: a named voice uses latest and the workspace default keeps its own saved version. Use 'legacy' ONLY when the user explicitly asks for the legacy/older voice generation; never choose it yourself.","enum":["latest","legacy"],"type":"string"}},"required":["guide_id","chat_id"," ⟨4 unchanged words⟩
Set the voiceover voice for every clip in aproject.project, or only for the clips in `clip_ids`. Use this to apply a clueprint's voice ( ⟨13 unchanged words⟩ clips to a specific voice in one call. When the user names specific clips, pass their ids in `clip_ids` so the other clips keep their voice. The voice is looked up by name + ⟨7 unchanged words⟩ Common engines: 'eleven' (ElevenLabs), 'cartesia', 'google'. ElevenLabs voices use the latest voice generation unless `voice_version` says otherwise. Leave it unset; pass "legacy" ONLY when the user explicitly asks for the legacy/older voice generation.
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"clip_ids":{"description":"Clip ids to change the voice on. Omit to change EVERY clip in the project.","items":{"type":"string"},"type":"array"},"project_id":{"description":"The project (guide) ⟨26 unchanged words⟩ (e.g. 'Alex', 'Sofia')","minLength":1,"type":"string"},"voice_version":{"description":"ElevenLabs voice generation. Leave unset (latest). Use 'legacy' ONLY when the user explicitly asks for the legacy/older voice generation; never choose it yourself. Ignored for non-ElevenLabs voices.","enum":["latest","legacy"],"type":"string"}},"required":[ ⟨5 unchanged words⟩
Changed the definition of "update_clips"
⟨236 unchanged words⟩ border?. Fields merge onto the clip's existing video_styling. - video_styling.border frames the recording, as the editor's Stroke panel does: omnibox (browser toolbar with a URL bar over the footage — e.g. { type: "omnibox", color: "#0052CC" }), browser (window chrome), notch (phone notch), solid (plain stroke; takes stroke), glass, animated, or none. Width and color default per type like the editor; a bare { color } restyles the current border. Concurrency: parallel-safe per clip — all property ⟨40 unchanged words⟩
⟨310 unchanged words⟩ :"number"},"color":{"description":"BorderFrame colorhexhex.(defaultsDefaults
Authorization not required, issuer https://auth.clueso.io/oauth/2.1
Added "get_recording_activity"; changed the definition of "generate_media" and "record_screen" (48 tools before, 49 now)
⟨676 unchanged words⟩ Image only. Which image model renders it. Default`gemini-3-pro-image-preview``gemini-3-pro-image` (Nano Banana Pro), a strong general ⟨5 unchanged words⟩ unless you want one of the specifics below.`gemini-3.1-flash-image-preview``gemini-3.1-flash-image` (Nano Banana 2), `gemini-nano-banana-2.1` (Nano Banana 2.1, Google's newest), `gemini-3.1-flash-lite-image` (Nano Banana 2 Lite, the fastest and cheapest) and `gemini-2.5-flash-image` are faster and cheaper. `gpt-image-2.5-flare` is OpenAI's ⟨76 unchanged words⟩ backend does with one.","enum":["gemini-3-pro-image-previewgemini-3-pro-image","gemini-3.1-flash-image-previewgemini-3.1-flash-image","gemini-nano-banana-2.1","gemini-3.1-flash-lite-image
Added "create_folder" and "get_embed"; changed the definition of "create_project", "export_project" and "get_export" (46 tools before, 48 now)
⟨58 unchanged words⟩ (use find(type='folders') to find folder IDs, or create_folder to make one) and set a description at creation time.
⟨27 unchanged words⟩ until status is completed — it then returnsdownload_url.download_url (or, for destination="embed", embed_url). The exportalso appears in the Exports tab in the editor.
Authorization not required, issuer https://auth.clueso.io/oauth/2.1
Authorization not required, issuer https://auth.clueso.io/oauth/2.1
Changed the definition of "generate_media" and "voiceover_batch"
⟨904 unchanged words⟩ text and triggers speech generation for the clip. If the clip's current voiceover_text (get_clip) has <SYNC_MARKER N> tokens, this text must carry each of them once, in order, or the animation is refused.","type":"string"},"width":{ ⟨37 unchanged words⟩
⟨124 unchanged words⟩ TTS for generate/set_and_generate runs async per clip after. Sync points: if get_clip's `voiceover_text` for the clip contains `<SYNC_MARKER N>` tokens (a screen recording's on-screen actions), your new text must carry every one of them, each exactly once and in order, placed where that action is narrated. Otherwise the entry is refused and nothing is written. Rephrase the words between the tokens freely.
Added "captions" (45 tools before, 46 now)
Edit captions — Read and edit a clip's CAPTIONS (subtitles) — what they say, where one caption ends and the next begins, when each shows, captions not tied to the voiceover, and translation. Captions are built from the clip's voiceover words: each caption is a run of words, addressed by its `start_word_index`. Start with action="list" on the clip — it returns `blocks` (voiceover captions: start_word_index, end_word_index, start/end in clip seconds, text, transcript_text, words, edited, retimed, mergeable), `manual_captions` ({caption_id, start, end, text}), `active_language` and `translated_languages`. Then…
Authorization not required, issuer https://auth.clueso.io/oauth/2.1
Added "get_export"; changed the definition of "export_project" and "run_script" (44 tools before, 45 now)
⟨7 unchanged words⟩ project. Returns immediately with an export_id; the exportpipeline runsrenders in thebackground.backgroundTheandusertakescan~5findmin.theCallrenderedget_export(export_id, wait_seconds=55)videountil status is completed — it then returns download_url. The export
Authorization not required, issuer https://auth.clueso.io/oauth/2.1
Authorization not required, issuer https://auth.clueso.io/oauth/2.1
Changed the definition of "add_elements", "find", "generate_media" and 5 more
before
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"propertyNames":{"type":"string"},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"elements":{"description":"Elements to add — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_id":{"description":"Clip ID — accepted but unused; the clip is located by clip_index for all element types.","type":"string"},"clip_index":{"description":"Zero-based clip index. Required unless hoisted into `defaults`.","maximum":9007199254740991,"minimum":0,"type":"integer"},"component_id":{"description":"Insert a SAVED COMPONENT exactly as stored — no AI generation, no restyling. This is how you reuse a component from your workspace verbatim (find one with find(type='element_components')). Components are workspace-scoped — there is no community library for them. A component is a fragment of a clip — one or more elements, possibly grouped — so this inserts ALL of them and returns every new element_id plus the animation's parameter_schema. Customise afterwards with update_elements(type_data.parameter_values). Mutually exclusive with element_type/type_data. To instead GENERATE a new animation using a component as a style reference, use element_type='animation' with type_data.base_component_id.","minLength":1,"type":"string"},"element_type":{"description":"One of: text, callout, rectangle, arrow, blur, spotlight, zoom, image, video, animation. Omit when using component_id.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"Disappear at (seconds)","minimum":0,"type":"number"},"group":{"description":"Put this element in a named group. The name is the handle: items in ONE call sharing the same name land in the SAME group, and a later call with that name adds to it. Use it to keep a unit together (a card and its label, a stat and its caption) so the user can move or hide it as one thing. Grouping never moves anything — coordinates stay absolute. Animations and AI-generated images are created in the background and cannot be grouped as they are added; add them first, then set `group` via update_elements.","minLength":1,"type":"string"},"height":{"description":"Height, pixels (0 allowed). For text the box's height always follows the measured text, so this reads as the space you are giving it: the text is centred in that space and layout.fits_height says whether it fit. Use type_data.fit to shrink the font into it.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Where the element lands in z-order, so you don't need a follow-up reorder_elements. `front` (default) puts it on top of what's already on the clip; `back` puts it behind everything — what a backdrop panel or a wash needs. `{before: element_id}` / `{after: element_id}` place it next to a specific element or group and put it in THAT node's group, which is the only way to position inside a group. Passing `group` as well changes what `front`/`back` address — see the Z-order notes in this tool's description. Placement is relative to the clip AS IT STANDS when the item is applied, so several `back` items in one call end up in REVERSE input order: send one backdrop per call, or list them back-to-front."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Animation keyframes (≥2 entries). Each entry has a timestamp plus one or more keyframable properties (position/size in canvas pixels; rotation°, opacity 0-100, scale, colors as #rrggbb[aa], etc. in native units). Easing: per-entry `interpolation` + optional per-property `interpolation_overrides`. Call get_element_schema to see which properties this element_type can keyframe. null is accepted and means none."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","maximum":9007199254740991,"minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"Label","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"start_time":{"description":"Appear at (seconds)","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific fields. Use the get_element_schema tool first to discover the shape for a given element_type. Omit when using component_id.","propertyNames":{"type":"string"},"type":"object"},"width":{"description":"Width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the WRAP width and it is kept as sent: the text wraps to it and the box stays this wide. (Height differs — the box's height follows the measured text.)","minimum":0,"type":"number"},"x":{"description":"X position in canvas pixels. `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right (callout, rectangle, arrow, blur, spotlight, image and animation all use the left edge). For zoom, use type_data.centerX instead.","type":"number"},"y":{"description":"Y position in canvas pixels — for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. Pair it with y_anchor (below), always.","type":"number"},"y_anchor":{"description":"TEXT only. ALWAYS send `y_anchor` ('top'|'center'|'bottom') when you set `y` on text, so the position states what you meant instead of inheriting the alignment origin. To centre in a row, pill, chip, card or button: pass the container's centre line as `y` with y_anchor:'center'. The server centres the MEASURED box — you cannot compute that height, since wrapping and `fit` change it. A write-time DIRECTIVE: never stored, never returned by a read, so send it on every call whose `y` means the same thing. (Also accepted inside type_data — older spelling; this one wins.)","enum":["top","center","bottom"],"type":"string"}},"type":"object"},"minItems":1,"type":"array"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping; plus `placement` {y_anchor, top, center, bottom} — where the box landed and what your `y` meant, emitted only when the origin was implicit or resolves to 'center'. Compare placement.center with a container's centre line to verify centring: a mis-anchored label is the one layout error nothing else reports), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"}},"required":["project_id","elements"],"type":"object"}Authorization not required, issuer https://auth.clueso.io/oauth/2.1
Changed the definition of "record_screen"
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"article_script_mode":{"description":"'exact': the article is used word for word. 'rewrite': the article may be reworded to match the recording. Overrides exact_article_script when both are set.","enum":["rewrite","exact"],"type":"string"},"chat_id":{"description":"Conversation context ID", ⟨80 unchanged words⟩ "exact_article_script":{"default":false,"description":"Legacy flag — prefer article_script_mode instead. Set to true when the article must be ⟨24 unchanged words⟩ "exact_video_script":{"default":false,"description":"Legacy flag — prefer video_script_mode instead. Set to true when the video narration must ⟨248 unchanged words⟩ this scene.","type":"string"},"article_script_mode":{"description":"How the article is treated for this scene. Overrides the top-level setting for this scene.","enum":["rewrite","exact"],"type":"string"},"clip_name":{"description":"Title for the clip ⟨82 unchanged words⟩ :"object"},"exact_article_script":{"description":"Legacy flag — prefer article_script_mode instead. When true, article_script for this scene is used
Changed the definition of "get_clip"
⟨786 unchanged words⟩ shapedata, imagedata, videodata, zoomdata, codedata,parent_id.parent_id, dropShadows. y_top is text-only and present ONLY when alignment ⟨120 unchanged words⟩ ","elements.videodata","elements.zoomdata","elements.codedata","elements.parent_id","elements.dropShadows"],"type":"string"},"type": ⟨6 unchanged words⟩
Changed the definition of "add_article_media", "add_audio", "add_clips" and 41 more
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"duration":{"description":"kind='gif' only: GIF ⟨14 unchanged words⟩ ,"gif"],"type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"mcp_upload_id":{"description":"kind='image' only: attach ⟨38 unchanged words⟩ number"}},"required":["kind","project_id","context","llm_model
Changed the definition of "add_article_media", "add_audio", "add_clips" and 41 more
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"duration":{"description":"kind='gif' only: GIF ⟨14 unchanged words⟩ ,"gif"],"type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"mcp_upload_id":{"description":"kind='image' only: attach ⟨38 unchanged words⟩ number"}},"required":["kind","project_id","context","llm_model
Certificate changed, valid to 2027-04-06
Authorization not required, issuer https://auth.clueso.io/oauth/2.1
Authorization not required, issuer https://auth.clueso.io/oauth/2.1
Added "get_comments"; changed the definition of "add_article_media", "add_audio", "add_clips" and 40 more (43 tools before, 44 now)
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"duration":{"description":" ⟨69 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties
Authorization not required, issuer https://auth.clueso.io/oauth/2.1
Added "get_script_job", "run_script" and "update_groups"; changed the definition of "add_elements", "analyze_audio", "get_clip" and 6 more (40 tools before, 43 now)
⟨12 unchanged words⟩ call. Replaces the per-element tool — always batch. SEND LESS. Most batches repeat themselves — the same clip_index, element_type, font_setting, alignment or gradientSetting on every item. Hoist those into `defaults` and send them once; each item then carries only what differs, and overrides any key it sets (type_data merges one level). On a 20-30 element batch this typically cuts the call by a quarter to a half. `returning` shapes the response the way `select` shapes a get_clip read. defaults: { clip_index: 2, element_type: 'text', type_data: { font_setting: { font: 'Inter', weight: '600' }, alignment: 'center' } } elements: [ { name: 'headline', x: 960, y: 400, type_data: { text: 'Hello', fontSize: 120 } }, ... ] Use after calling get_element_schema to confirm the type_data ⟨82 unchanged words⟩ want a NEW variant rather than that component. Z-order: list position IS z-index — later renders on top — and a new element goes on top by default. Pass `insert_at: 'back'` to put it behind what's already there, or `insert_at: {before: id}` / `{after: id}` to land next to a specific element — that also puts the new element in THAT node's group, which is the only way to place inside one. `reorder_elements` rewrites the whole order in one call, grouped clips included. Grouping: pass `group: "<name>"` to keep ⟨87 unchanged words⟩ together at the lowest member's slot. Concurrency:
Authorization not required, issuer https://auth.clueso.io/oauth/2.1
Certificate recorded, valid to 2027-03-08
Authorization not required, issuer https://auth.clueso.io/oauth/2.1
First tool surface recorded: 40 tools (server version 1.0.0)
Showing the latest 30 events. The API returns up to 500 and filters by kind: ?kind=tool_surface_changed
CreateRecords the product on a mock (Code record) as a screen-recording clip in a project.Creates blank placeholder clips, registersjob entities, and sends the job to AVS. ⟨21 unchanged words⟩ Article placeholders are also inserted automatically into plainDoc. Check how the recording is going with get_recording_activity(project_id). A recording can take hours, so check once rather than polling in a loop. Requires the Auto-Recording add-on and per-workspace sign-in credentials ⟨22 unchanged words⟩
⟨775 unchanged words⟩ "minItems":1,"type":"array"},"target":{"description":"Which recorder to use when the workspace has both Code record and Browser record: 'code' for a mock of the product, 'browser' for the real product — from the user's request, or their answer when it didn't say. Omit otherwise.","enum":["code","browser"],"type":"string"},"video_intention":{"default":"","description":" ⟨149 unchanged words⟩
Get Recording Activity — Status of this workspace's auto-recordings, and how many recording generations this billing cycle has used. The same view as the Recordings page in the Clueso app. Returns { usage, rows, total, truncated }. `usage` is { cycle_start, cycle_end, generations_used, generations_limit (null: no cap), active_recordings, active_recordings_limit }. Each row is one project with its latest recording's `state` (in_progress, done or failed), the `recorders` used on it (Code-record, Browser-record), this cycle's `generations` for it, `last_activity_at` and `created_by`. A row with `kind: "run"` is a Brows…
⟨11 unchanged words⟩ default false)","type":"boolean"},"destination":{"description":"Where the render goes: \"download\" (mp4 file, default) or \"embed\" (publish/update the project's embeddable player; its URL never changes). Publishing an embed needs an editor or admin role.","enum":["download","embed"],"type":"string"},"fps":{"anyOf":[{"const":24, ⟨42 unchanged words⟩
⟨156 unchanged words⟩ ). Calling get_export again mints a fresh link. An embed publish (export_project with destination="embed") also completes with `embed_url` (permanent) and `embed_code` (a ready-to-paste <iframe>), alongside its download_url.
Create folder — Create a folder in the workspace, optionally inside another folder, to organize projects. Returns { folder_id, name, parent_folder_id, already_existed }. If a folder with the same name (case-insensitive) already exists in that parent, it is returned with already_existed=true instead of creating a duplicate — so it is safe to call to "ensure" a folder exists. Put projects in it with create_project(folder_id=...) or update_project(project_id, folder_id=...). List existing folders with find(type='folders').
Get embed URL — Get a project's embeddable video player: `embed_url` (permanent) and `embed_code` (a ready-to-paste <iframe>, the same snippet the editor's Share → Embed copies). Use it to put a Clueso video in a website, help center or LMS lesson. Returns { project_id, published, embed_url?, embed_code?, latest_publish? }. • published=false — the project has never been published as an embed. Call export_project(project_id, destination="embed"), then get_export(export_id) until completed. • published=true — the URL is live. After editing the project, re-publish with export_project(destination="embed"): …
⟨224 unchanged words⟩ of seconds each and will eat a budgetfast.fast, and get_export holds for its wait_seconds (pass 0 inside a script). analyze_audio in 'features' mode is ~1s and is ⟨62 unchanged words⟩
Get export status — Check on a project's video export, waiting for it to finish. Pass exactly one of `export_id` (the one export_project returned) or `project_id` (that project's most recently REQUESTED export, which may still be in progress). Returns { export_id, project_id, status, progress, requested_at, finished_at? }. `status` is queued, waitlisted or pending (not started yet), inprogress (`progress` is 0-100), completed, failed, or stalled (stuck for hours; start a new export). The call holds open until the export completes, fails or stalls, or `wait_seconds` runs out — then it returns the current status …
after
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"propertyNames":{"type":"string"},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"elements":{"description":"Elements to add — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_id":{"description":"Clip ID — accepted but unused; the clip is located by clip_index for all element types.","type":"string"},"clip_index":{"description":"Zero-based clip index. Required unless hoisted into `defaults`.","maximum":9007199254740991,"minimum":0,"type":"integer"},"component_id":{"description":"Insert a SAVED COMPONENT exactly as stored — no AI generation, no restyling. This is how you reuse a component from your workspace verbatim (find one with find(type='element_components')). Components are workspace-scoped — there is no community library for them. A component is a fragment of a clip — one or more elements, possibly grouped — so this inserts ALL of them and returns every new element_id plus the animation's parameter_schema. Customise afterwards with update_elements(type_data.parameter_values). Mutually exclusive with element_type/type_data. To instead GENERATE a new animation using a component as a style reference, use element_type='animation' with type_data.base_component_id.","minLength":1,"type":"string"},"element_type":{"description":"One of: text, callout, rectangle, arrow, blur, spotlight, zoom, image, video, animation. Omit when using component_id.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"Disappear at (seconds)","minimum":0,"type":"number"},"group":{"description":"Put this element in a named group. The name is the handle: items in ONE call sharing the same name land in the SAME group, and a later call with that name adds to it. Use it to keep a unit together (a card and its label, a stat and its caption) so the user can move or hide it as one thing. Grouping never moves anything — coordinates stay absolute. Animations and AI-generated images are created in the background and cannot be grouped as they are added; add them first, then set `group` via update_elements.","minLength":1,"type":"string"},"height":{"description":"Height, pixels (0 allowed). For text the box's height always follows the measured text, so this reads as the space you are giving it: the text is centred in that space and layout.fits_height says whether it fit. Use type_data.fit to shrink the font into it.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Where the element lands in z-order, so you don't need a follow-up reorder_elements. `front` (default) puts it on top of what's already on the clip; `back` puts it behind everything — what a backdrop panel or a wash needs. `{before: element_id}` / `{after: element_id}` place it next to a specific element or group and put it in THAT node's group, which is the only way to position inside a group. Passing `group` as well changes what `front`/`back` address — see the Z-order notes in this tool's description. Placement is relative to the clip AS IT STANDS when the item is applied, so several `back` items in one call end up in REVERSE input order: send one backdrop per call, or list them back-to-front."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Animation keyframes (≥2 entries). Each entry has a timestamp plus one or more keyframable properties (position/size in canvas pixels; rotation°, opacity 0-100, scale, colors as #rrggbb[aa], etc. in native units). Easing: per-entry `interpolation` + optional per-property `interpolation_overrides`. Call get_element_schema to see which properties this element_type can keyframe. null is accepted and means none."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","maximum":9007199254740991,"minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"Label","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"start_time":{"description":"Appear at (seconds)","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific fields. Use the get_element_schema tool first to discover the shape for a given element_type. Omit when using component_id.","propertyNames":{"type":"string"},"type":"object"},"width":{"description":"Width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the WRAP width and it is kept as sent: the text wraps to it and the box stays this wide. (Height differs — the box's height follows the measured text.)","minimum":0,"type":"number"},"x":{"description":"X position in canvas pixels. `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right (callout, rectangle, arrow, blur, spotlight, image and animation all use the left edge). For zoom, use type_data.centerX instead.","type":"number"},"y":{"description":"Y position in canvas pixels — for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. Pair it with y_anchor (below), always.","type":"number"},"y_anchor":{"description":"TEXT only. ALWAYS send `y_anchor` ('top'|'center'|'bottom') when you set `y` on text, so the position states what you meant instead of inheriting the alignment origin. To centre in a row, pill, chip, card or button: pass the container's centre line as `y` with y_anchor:'center'. The server centres the MEASURED box — you cannot compute that height, since wrapping and `fit` change it. A write-time DIRECTIVE: never stored, never returned by a read, so send it on every call whose `y` means the same thing. (Also accepted inside type_data — older spelling; this one wins.)","enum":["top","center","bottom"],"type":"string"}},"type":"object"},"minItems":1,"type":"array"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping; plus `placement` {y_anchor, top, center, bottom} — where the box landed and what your `y` meant, emitted only when the origin was implicit or resolves to 'center'. Compare placement.center with a container's centre line to verify centring: a mis-anchored label is the one layout error nothing else reports), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). A text element measured on a fallback face is listed in top-level `font_warnings` whichever you choose. ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"}},"required":["project_id","elements"],"type":"object"}⟨29 unchanged words⟩ clueprints | workspaces • backgrounds | voices | fonts | image_gen_style_packs | element_components (voices carry `gender`, `tags`, ⟨186 unchanged words⟩ so you can tell what it sounds like. (fonts = the typefaces THIS WORKSPACE has uploaded as brand fonts, each with `id`, `name` and `src`. Pass a returned `name` as a text element's `font_setting.font` — that is the whole contract; you never pass the url. This list is the ONLY way to know which non-Google families actually exist here: a font_setting naming a family in neither this list nor Google Fonts silently renders Inter, with no error and a confident-looking `layout.text_width_px` measured against the wrong face. Check here before building a layout on a brand typeface.) (image_gen_style_packs = generation style presets for generate_media ⟨390 unchanged words⟩
⟨335 unchanged words⟩ clueprints","workspaces","backgrounds","voices","fonts","image_gen_style_packs","element_components","images","videos"," ⟨28 unchanged words⟩
{"$schema":"https://json-schema.org/draft/2020-12/schema","additionalProperties":{},"properties":{"animation_setting":{"description":"Image ⟨170 unchanged words⟩ the same default the editor creates with.","exclusiveMinimumminimum":0,"type":"number"},"preset" ⟨141 unchanged words⟩ the same default the editor creates with.","exclusiveMinimumminimum":0,"type":"number"},"preset" ⟨23 unchanged words⟩ "object"}},"type":"object"},"aspect_ratio":{"description":"Image only. Overrides the ratio inferred from width/height. Leave it off and the box decides, which is usually what you want — set it when the box is a placeholder and you know the shape you need. Still clamped to what the model accepts: gpt-image-1/1.5 only do 1:1, 4:3 and 3:4; the gpt-image-2.5 pair adds 16:9 and 9:16; gpt-image-2 has no 3:2 or 2:3, so those become 4:3 and 3:4. The response does not report the ratio used.","enum":["1:1","4:3","3:4","16:9","9:16","3:2","2:3"],"type":"string"},"background":{"anyOf":[{"type":"boolean ⟨72 unchanged words⟩ to, since the older pair cannot frame wide. Asking for transparency in the PROMPT instead does not work — the model paints a grey-and-white checkerboard as real pixels."},"base_component_id":{"description":"Animation only. ⟨369 unchanged words⟩
⟨11 unchanged words⟩ project. - target="element": removes an elementfrom(requiresaelement_id,clipor(requireselement_ids for several). clip_index+iselement_id)optional — the element is located by id; pass it only as a hint - target="group": removes a GROUP node ⟨102 unchanged words⟩
⟨4 unchanged words⟩ :{"clip_index":{"description":"Zero-based clipindexindex.(requiredRequired for target='group'; for target='element')an optional hint — the element is found by id either way","maximum":9007199254740991,"minimum":0,"type ⟨2 unchanged words⟩ :{"description":"Element ID to remove (requiredtarget='element';foror use element_ids)","type":"string"},"element_ids":{"description":"Several element IDs to remove (target='element'). Removed one at a time, so a failure part-way leaves the earlier ones removed; retrying the same list is safe — ids already gone come back as already_absent. For a large batch prefer remove_elements, which lands in one save","items":{"type":"string"},"type":"array"},"group_id":{"description":"Group ID ⟨67 unchanged words⟩
⟨344 unchanged words⟩ ])` says what is still being written.ItAissleepclampedlongertothan the script's remaining time,soFAILSathelongrunsleepwithendsathebudgetrunerror rather than overrunning it or returning early (a run that hands off to a background job gets the job budget, and a sleep already waiting picks that up) — and it holds one of the few ⟨534 unchanged words⟩ : 200 tool calls, 45s of run time inline and 5 minutes once it hands off to a job, 16KB each of logged output and returned ⟨18 unchanged words⟩
⟨147 unchanged words⟩ "},"timeout_seconds":{"description":"Wall-clock budget for the SCRIPT — separate from wait_seconds, which is only how long this call stays open. By default45s.aRaisescriptitgetsonly45sifwhileyouryouclient'sareownwaitingrequestontimeoutit, and islongerraised—tomany
before
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"propertyNames":{"type":"string"},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping; plus `placement` {y_anchor, top, center, bottom} — where the box landed and what your `y` meant, emitted only when the origin was implicit or resolves to 'center'. Compare placement.center with a container's centre line to verify centring: a mis-anchored label is the one layout error nothing else reports), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"},"updates":{"description":"Element updates — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_index":{"description":"Zero-based clip index. OPTIONAL — `element_id` is unique within the project, so the server finds the clip itself. Pass it only as a fast-path hint; a wrong one is corrected rather than rejected.","maximum":9007199254740991,"minimum":0,"type":"integer"},"element_id":{"description":"Element ID (from get_clip)","type":"string"},"element_type":{"description":"Required only when providing type_data. Use get_element_schema in 'update' mode to see fields.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"New time the element disappears, in seconds from the start of the clip.","minimum":0,"type":"number"},"fit_to_lines":{"description":"Text elements only. Target line count for the element. After this entry's regular updates land, the server widens (then font-shrinks if widen can't fit) the element so the rendered text wraps to at most N lines without overlapping its neighbours. Uses the same compute path as director's fit_text_layout.","maximum":9007199254740991,"minimum":1,"type":"integer"},"group":{"description":"Move this element into a named group, creating it if this clip has none by that name. Pass an empty string to pull it back out to the clip root. Omit to leave grouping unchanged. Grouping never moves anything — coordinates stay absolute.","type":"string"},"height":{"description":"New height, pixels (0 allowed). For text, the space you are giving it — the box resizes to the measured text. See layout.fits_height in the response.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Move this element in z-order, without rebuilding it. `front`/`back` are the ends of the group it is already in (the clip itself for an ungrouped element), so they never change what it belongs to — with one exception: for an element that is ALONE in its group they move the GROUP within its parent scope, because positioning a sole member inside its own group would be a no-op. `{before: element_id}` / `{after: element_id}` sit it next to that element or group and put it in THAT node's group — which is how you move something into or out of a group and position it in one call. Applied after `group` when both are passed."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Set keyframes for this element. Only the property tracks present in your entries are replaced — other tracks (color/rotation/typography/…) are preserved. Pass null OR an empty array to clear ALL keyframes. Same per-entry shape as add_elements; see get_element_schema for the keyframable properties of this element_type."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","maximum":9007199254740991,"minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"New label for the element.","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"save_as_component":{"description":"Animation elements only. Set true to save the current state as a reusable animation component (auto-named from the animation title), or pass a string to name it explicitly. Save runs AFTER any position/type_data updates land. Returns the component_id alongside the per-update result.","type":["boolean","string"]},"start_time":{"description":"New time the element appears, in seconds from the start of the clip.","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific updates. When provided, element_type is required. Get the schema via get_element_schema.","propertyNames":{"type":"string"},"type":"object"},"width":{"description":"New width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the wrap width and re-wraps the text.","minimum":0,"type":"number"},"x":{"description":"New X (canvas pixels). Same origin as add_elements: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right. Changing alignment in the same call re-anchors x to the new edge.","type":"number"},"y":{"description":"New Y (canvas pixels) — for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. See y_anchor.","type":"number"},"y_anchor":{"description":"TEXT only. ALWAYS send `y_anchor` ('top'|'center'|'bottom') when you set `y` on text, so the position states what you meant instead of inheriting the alignment origin. Omit `y` and it anchors to the element's CURRENT box, holding a centred label centred across a re-measure. Not stored — state it on every call. (Also accepted inside type_data; this one wins.)","enum":["top","center","bottom"],"type":"string"}},"required":["element_id"],"type":"object"},"minItems":1,"type":"array"}},"required":["project_id","updates"],"type":"object"}after
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"propertyNames":{"type":"string"},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping; plus `placement` {y_anchor, top, center, bottom} — where the box landed and what your `y` meant, emitted only when the origin was implicit or resolves to 'center'. Compare placement.center with a container's centre line to verify centring: a mis-anchored label is the one layout error nothing else reports), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). A text element measured on a fallback face is listed in top-level `font_warnings` whichever you choose. ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"},"updates":{"description":"Element updates — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_index":{"description":"Zero-based clip index. OPTIONAL — `element_id` is unique within the project, so the server finds the clip itself. Pass it only as a fast-path hint; a wrong one is corrected rather than rejected.","maximum":9007199254740991,"minimum":0,"type":"integer"},"element_id":{"description":"Element ID (from get_clip)","type":"string"},"element_type":{"description":"Required only when providing type_data. Use get_element_schema in 'update' mode to see fields.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"New time the element disappears, in seconds from the start of the clip.","minimum":0,"type":"number"},"fit_to_lines":{"description":"Text elements only. Target line count for the element. After this entry's regular updates land, the server widens (then font-shrinks if widen can't fit) the element so the rendered text wraps to at most N lines without overlapping its neighbours. Uses the same compute path as director's fit_text_layout.","maximum":9007199254740991,"minimum":1,"type":"integer"},"group":{"description":"Move this element into a named group, creating it if this clip has none by that name. Pass an empty string to pull it back out to the clip root. Omit to leave grouping unchanged. Grouping never moves anything — coordinates stay absolute.","type":"string"},"height":{"description":"New height, pixels (0 allowed). For text, the space you are giving it — the box resizes to the measured text. See layout.fits_height in the response.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Move this element in z-order, without rebuilding it. `front`/`back` are the ends of the group it is already in (the clip itself for an ungrouped element), so they never change what it belongs to — with one exception: for an element that is ALONE in its group they move the GROUP within its parent scope, because positioning a sole member inside its own group would be a no-op. `{before: element_id}` / `{after: element_id}` sit it next to that element or group and put it in THAT node's group — which is how you move something into or out of a group and position it in one call. Applied after `group` when both are passed."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Set keyframes for this element. Only the property tracks present in your entries are replaced — other tracks (color/rotation/typography/…) are preserved. Pass null OR an empty array to clear ALL keyframes. Same per-entry shape as add_elements; see get_element_schema for the keyframable properties of this element_type."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","maximum":9007199254740991,"minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"New label for the element.","type":"string"},"parent_id":{"description":"The parent `get_clip` reports on this element: a group id, or the clip id for an ungrouped element. Accepted so a read round-trips unedited — the element's current parent is a no-op and leaves z-order alone. A different group id on this element's clip moves it there (the group must have a name); the clip id or null moves it to the root. Use `group` when you are choosing a group — the name is the handle. `group` wins if both are sent.","type":["string","null"]},"rotation":{"description":"Rotation in degrees","type":"number"},"save_as_component":{"description":"Animation elements only. Set true to save the current state as a reusable animation component (auto-named from the animation title), or pass a string to name it explicitly. Save runs AFTER any position/type_data updates land. Returns the component_id alongside the per-update result.","type":["boolean","string"]},"start_time":{"description":"New time the element appears, in seconds from the start of the clip.","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific updates. When provided, element_type is required. Get the schema via get_element_schema.","propertyNames":{"type":"string"},"type":"object"},"width":{"description":"New width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the wrap width and re-wraps the text.","minimum":0,"type":"number"},"x":{"description":"New X (canvas pixels). Same origin as add_elements: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right. Changing alignment in the same call re-anchors x to the new edge.","type":"number"},"y":{"description":"New Y (canvas pixels) — for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. See y_anchor.","type":"number"},"y_anchor":{"description":"TEXT only. ALWAYS send `y_anchor` ('top'|'center'|'bottom') when you set `y` on text, so the position states what you meant instead of inheriting the alignment origin. Omit `y` and it anchors to the element's CURRENT box, holding a centred label centred across a re-measure. Not stored — state it on every call. (Also accepted inside type_data; this one wins.)","enum":["top","center","bottom"],"type":"string"}},"required":["element_id"],"type":"object"},"minItems":1,"type":"array"}},"required":["project_id","updates"],"type":"object"}⟨168 unchanged words⟩ ,"blur"],"type":"string"},"required":["offsetX","offsetY","scaleX","scaleY","rotation","rotateX","rotateY","opacity","blur"],"type":"object"},"blur":{"description" ⟨76 unchanged words⟩ ,"blur"],"type":"string"},"required":["offsetX","offsetY","scaleX","scaleY","rotation","rotateX","rotateY","opacity","blur"],"type":"object"},"offsetX":{"description" ⟨470 unchanged words⟩ code alone isn't enough to act on). A text element measured on a fallback face is listed in top-level `font_warnings` whichever you choose. ERROR-severity findings always come back with their full ⟨49 unchanged words⟩
Upload one or more files to Clueso.Three modesPick—apickmode by client + where the file lives: 0. **files** — ChatGPT only: files the user attached in the conversation. ChatGPT fills each entry (download_url + file_id) itself; pass the attachments here rather than asking the user to re-upload. Returns one mcp_upload_id per file. 1. **file_name** — HOSTED upload, the default for ⟨366 unchanged words⟩
⟨64 unchanged words⟩ file from","type":"string"},"files":{"description":"ChatGPT file attachments, one entry per file (filled by ChatGPT).","items":{"properties":{"download_url":{"description":"Temporary download URL supplied by ChatGPT","type":"string"},"file_id":{"description":"ChatGPT file id","type":"string"},"file_name":{"type":"string"},"mime_type":{"type":"string"}},"required":["download_url","file_id"],"type":"object"},"type":"array"},"request_hosted_upload":{"description":"If true, returns ⟨22 unchanged words⟩
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"fade_in":{"description":"Fade-in duration in seconds. ⟨97 unchanged words⟩ "minimum":0,"type":"number"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"loop":{"description":"Loop the track if ⟨237 unchanged words⟩ number"}},"required":["project_id","type","context","llm_model"],"type":"object"}
⟨260 unchanged words⟩ :"object"},"type":"array"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"cuts":{"description":"kind='video' only: per-clip ⟨77 unchanged words⟩ ,"video"],"type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"mcp_upload_id":{"description":"kind='pptx' or 'video': ⟨98 unchanged words⟩ string"}},"required":["kind","project_id","context","llm_model"],"type":"object"}
⟨46 unchanged words⟩ "minLength":1,"type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"parent_comment_id":{"description":"Reply to this comment ⟨44 unchanged words⟩ string"}},"required":["project_id","comment_text","context","llm_model"],"type":"object"}
before
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"propertyNames":{"type":"string"},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"elements":{"description":"Elements to add — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_id":{"description":"Clip ID — accepted but unused; the clip is located by clip_index for all element types.","type":"string"},"clip_index":{"description":"Zero-based clip index. Required unless hoisted into `defaults`.","maximum":9007199254740991,"minimum":0,"type":"integer"},"component_id":{"description":"Insert a SAVED COMPONENT exactly as stored — no AI generation, no restyling. This is how you reuse a component from your workspace verbatim (find one with find(type='element_components')). Components are workspace-scoped — there is no community library for them. A component is a fragment of a clip — one or more elements, possibly grouped — so this inserts ALL of them and returns every new element_id plus the animation's parameter_schema. Customise afterwards with update_elements(type_data.parameter_values). Mutually exclusive with element_type/type_data. To instead GENERATE a new animation using a component as a style reference, use element_type='animation' with type_data.base_component_id.","minLength":1,"type":"string"},"element_type":{"description":"One of: text, callout, rectangle, arrow, blur, spotlight, zoom, image, video, animation. Omit when using component_id.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"Disappear at (seconds)","minimum":0,"type":"number"},"group":{"description":"Put this element in a named group. The name is the handle: items in ONE call sharing the same name land in the SAME group, and a later call with that name adds to it. Use it to keep a unit together (a card and its label, a stat and its caption) so the user can move or hide it as one thing. Grouping never moves anything — coordinates stay absolute. Animations and AI-generated images are created in the background and cannot be grouped as they are added; add them first, then set `group` via update_elements.","minLength":1,"type":"string"},"height":{"description":"Height, pixels (0 allowed). For text the box's height always follows the measured text, so this reads as the space you are giving it: the text is centred in that space and layout.fits_height says whether it fit. Use type_data.fit to shrink the font into it.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Where the element lands in z-order, so you don't need a follow-up reorder_elements. `front` (default) puts it on top of what's already on the clip; `back` puts it behind everything — what a backdrop panel or a wash needs. `{before: element_id}` / `{after: element_id}` place it next to a specific element or group and put it in THAT node's group, which is the only way to position inside a group. Passing `group` as well changes what `front`/`back` address — see the Z-order notes in this tool's description. Placement is relative to the clip AS IT STANDS when the item is applied, so several `back` items in one call end up in REVERSE input order: send one backdrop per call, or list them back-to-front."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Animation keyframes (≥2 entries). Each entry has a timestamp plus one or more keyframable properties (position/size in canvas pixels; rotation°, opacity 0-100, scale, colors as #rrggbb[aa], etc. in native units). Easing: per-entry `interpolation` + optional per-property `interpolation_overrides`. Call get_element_schema to see which properties this element_type can keyframe. null is accepted and means none."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","maximum":9007199254740991,"minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"Label","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"start_time":{"description":"Appear at (seconds)","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific fields. Use the get_element_schema tool first to discover the shape for a given element_type. Omit when using component_id.","propertyNames":{"type":"string"},"type":"object"},"width":{"description":"Width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the WRAP width and it is kept as sent: the text wraps to it and the box stays this wide. (Height differs — the box's height follows the measured text.)","minimum":0,"type":"number"},"x":{"description":"X position in canvas pixels. `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right (callout, rectangle, arrow, blur, spotlight, image and animation all use the left edge). For zoom, use type_data.centerX instead.","type":"number"},"y":{"description":"Y position in canvas pixels — for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. Pair it with y_anchor (below), always.","type":"number"},"y_anchor":{"description":"TEXT only. ALWAYS send `y_anchor` ('top'|'center'|'bottom') when you set `y` on text, so the position states what you meant instead of inheriting the alignment origin. To centre in a row, pill, chip, card or button: pass the container's centre line as `y` with y_anchor:'center'. The server centres the MEASURED box — you cannot compute that height, since wrapping and `fit` change it. A write-time DIRECTIVE: never stored, never returned by a read, so send it on every call whose `y` means the same thing. (Also accepted inside type_data — older spelling; this one wins.)","enum":["top","center","bottom"],"type":"string"}},"type":"object"},"minItems":1,"type":"array"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping; plus `placement` {y_anchor, top, center, bottom} — where the box landed and what your `y` meant, emitted only when the origin was implicit or resolves to 'center'. Compare placement.center with a container's centre line to verify centring: a mis-anchored label is the one layout error nothing else reports), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"}},"required":["project_id","elements","context","llm_model"],"type":"object"}after
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"propertyNames":{"type":"string"},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"elements":{"description":"Elements to add — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_id":{"description":"Clip ID — accepted but unused; the clip is located by clip_index for all element types.","type":"string"},"clip_index":{"description":"Zero-based clip index. Required unless hoisted into `defaults`.","maximum":9007199254740991,"minimum":0,"type":"integer"},"component_id":{"description":"Insert a SAVED COMPONENT exactly as stored — no AI generation, no restyling. This is how you reuse a component from your workspace verbatim (find one with find(type='element_components')). Components are workspace-scoped — there is no community library for them. A component is a fragment of a clip — one or more elements, possibly grouped — so this inserts ALL of them and returns every new element_id plus the animation's parameter_schema. Customise afterwards with update_elements(type_data.parameter_values). Mutually exclusive with element_type/type_data. To instead GENERATE a new animation using a component as a style reference, use element_type='animation' with type_data.base_component_id.","minLength":1,"type":"string"},"element_type":{"description":"One of: text, callout, rectangle, arrow, blur, spotlight, zoom, image, video, animation. Omit when using component_id.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"Disappear at (seconds)","minimum":0,"type":"number"},"group":{"description":"Put this element in a named group. The name is the handle: items in ONE call sharing the same name land in the SAME group, and a later call with that name adds to it. Use it to keep a unit together (a card and its label, a stat and its caption) so the user can move or hide it as one thing. Grouping never moves anything — coordinates stay absolute. Animations and AI-generated images are created in the background and cannot be grouped as they are added; add them first, then set `group` via update_elements.","minLength":1,"type":"string"},"height":{"description":"Height, pixels (0 allowed). For text the box's height always follows the measured text, so this reads as the space you are giving it: the text is centred in that space and layout.fits_height says whether it fit. Use type_data.fit to shrink the font into it.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Where the element lands in z-order, so you don't need a follow-up reorder_elements. `front` (default) puts it on top of what's already on the clip; `back` puts it behind everything — what a backdrop panel or a wash needs. `{before: element_id}` / `{after: element_id}` place it next to a specific element or group and put it in THAT node's group, which is the only way to position inside a group. Passing `group` as well changes what `front`/`back` address — see the Z-order notes in this tool's description. Placement is relative to the clip AS IT STANDS when the item is applied, so several `back` items in one call end up in REVERSE input order: send one backdrop per call, or list them back-to-front."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Animation keyframes (≥2 entries). Each entry has a timestamp plus one or more keyframable properties (position/size in canvas pixels; rotation°, opacity 0-100, scale, colors as #rrggbb[aa], etc. in native units). Easing: per-entry `interpolation` + optional per-property `interpolation_overrides`. Call get_element_schema to see which properties this element_type can keyframe. null is accepted and means none."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","maximum":9007199254740991,"minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"Label","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"start_time":{"description":"Appear at (seconds)","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific fields. Use the get_element_schema tool first to discover the shape for a given element_type. Omit when using component_id.","propertyNames":{"type":"string"},"type":"object"},"width":{"description":"Width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the WRAP width and it is kept as sent: the text wraps to it and the box stays this wide. (Height differs — the box's height follows the measured text.)","minimum":0,"type":"number"},"x":{"description":"X position in canvas pixels. `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right (callout, rectangle, arrow, blur, spotlight, image and animation all use the left edge). For zoom, use type_data.centerX instead.","type":"number"},"y":{"description":"Y position in canvas pixels — for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. Pair it with y_anchor (below), always.","type":"number"},"y_anchor":{"description":"TEXT only. ALWAYS send `y_anchor` ('top'|'center'|'bottom') when you set `y` on text, so the position states what you meant instead of inheriting the alignment origin. To centre in a row, pill, chip, card or button: pass the container's centre line as `y` with y_anchor:'center'. The server centres the MEASURED box — you cannot compute that height, since wrapping and `fit` change it. A write-time DIRECTIVE: never stored, never returned by a read, so send it on every call whose `y` means the same thing. (Also accepted inside type_data — older spelling; this one wins.)","enum":["top","center","bottom"],"type":"string"}},"type":"object"},"minItems":1,"type":"array"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping; plus `placement` {y_anchor, top, center, bottom} — where the box landed and what your `y` meant, emitted only when the origin was implicit or resolves to 'center'. Compare placement.center with a container's centre line to verify centring: a mis-anchored label is the one layout error nothing else reports), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"}},"required":["project_id","elements"],"type":"object"}⟨29 unchanged words⟩ "minimum":0,"type":"integer"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"occurrence":{"default":1,"description":"1-indexed ⟨77 unchanged words⟩ ,"required":["project_id","word","timestamp_seconds","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"file_url":{"description":"Public http(s) ⟨20 unchanged words⟩ private/loopback ranges.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"mcp_upload_id":{"description":"MCP upload ID from ⟨112 unchanged words⟩ type":"object"}},"required":["mode","context","llm_model"],"type":"object"}
⟨8 unchanged words⟩ to auto-sync.","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"Project ID.","type":"string"}},"required":["project_id","clip_id","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"upload_token":{"description":"The upload_token returned by ⟨3 unchanged words⟩ type":"string"}},"required":["upload_token","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"description":{"description":"Description of what this clueprint does","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"name":{"description":"Name of the clueprint ⟨34 unchanged words⟩ string"}},"required":["name","visibility","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"description":{"description":"Optional project description (max ~500 chars)"," ⟨22 unchanged words⟩ workspace root.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"title":{"description":"Project title","type":"string"}},"required":["context","llm_model"],"type":"object"}
⟨17 unchanged words⟩ if omitted.","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"source_clip_id":{"description":"ID of the clip ⟨34 unchanged words⟩ string"}},"required":["target_project_id","source_clip_id","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"folder_id":{"description":"Place the duplicate in ⟨18 unchanged words⟩ workspace root.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"new_title":{"description":"Override the title of ⟨11 unchanged words⟩ type":"string"}},"required":["source_project_id","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"texts":{"description":"Array of voiceover texts ⟨12 unchanged words⟩ type":"array"}},"required":["texts","context","llm_model"],"type":"object"}
⟨11 unchanged words⟩ default false)","type":"boolean"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"fps":{"anyOf":[{"const":24, ⟨11 unchanged words⟩ "Frames per second (default 30)"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project ID to ⟨14 unchanged words⟩ type":"string"}},"required":["project_id","context","llm_model"],"type":"object"}
⟨13 unchanged words⟩ e.g. 'blue'.","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"creator_id":{"description":"clueprints only: filter ⟨97 unchanged words⟩ "minimum":1,"type":"integer"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"max_duration":{"description":"stock videos + freesound ⟨236 unchanged words⟩ type":"string"}},"required":["type","context","llm_model"],"type":"object"}
⟨491 unchanged words⟩ element into.","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"end_time":{"description":"Disappear at (seconds) ⟨27 unchanged words⟩ ,"animation"],"type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"mcp_upload_id":{"description":"mcp_upload_id from the upload ⟨283 unchanged words⟩ ","x","y","width","height","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"Project ID","type":"string"}},"required":["project_id","context","llm_model"],"type":"object"}
⟨11 unchanged words⟩ "minimum":0,"type":"integer"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"element_ids":{"description":"WHICH element rows to ⟨41 unchanged words⟩ :"string"},"type":"array"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project ID", ⟨857 unchanged words⟩ array"}},"required":["project_id","clip_index","context","llm_model"],"type":"object"}
⟨8 unchanged words⟩ the clueprint.","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"file_paths":{"description":"include='files' only: relative ⟨38 unchanged words⟩ files","all"],"type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"}},"required":["clueprint_id","context","llm_model"],"type":"object"}
⟨20 unchanged words⟩ ones included.","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"include_resolved":{"description":"Include threads already marked ⟨16 unchanged words⟩ work outstanding.","type":"boolean"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project (guide) ID","type":"string"}},"required":["project_id","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"}},"required":["context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"element_type":{"description":"One of: text, ⟨106 unchanged words⟩ ,"json"],"type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"mode":{"default":"add","description": ⟨12 unchanged words⟩ type":"string"}},"required":["element_type","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project ID","type":"string"}},"required":["project_id","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"job_id":{"description":"From the run_script reply. ⟨20 unchanged words⟩ }|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"wait_seconds":{"description":"How long to wait ⟨17 unchanged words⟩ type":"integer"}},"required":["job_id","context","llm_model"],"type":"object"}
⟨7 unchanged words⟩ context ID","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"custom_instructions":{"default":"","description":" ⟨165 unchanged words⟩ : en)","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"preceding_clip_id":{"description":"Clip ID after which ⟨521 unchanged words⟩ :["guide_id","chat_id","preceding_clip_id","scenes","context","llm_model"],"type":"object"}
⟨13 unchanged words⟩ "minimum":0,"type":"integer"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project ID","type":"string"}},"required":["project_id","clip_index","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"Project ID","type ⟨42 unchanged words⟩ array"}},"required":["project_id","removals","context","llm_model"],"type":"object"}
⟨14 unchanged words⟩ "minimum":0,"type":"integer"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"element_id":{"description":"Element ID to remove ⟨33 unchanged words⟩ whole subtree.","type":"boolean"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"music_id":{"description":"Music/SFX track ID to ⟨27 unchanged words⟩ string"}},"required":["project_id","target","context","llm_model"],"type":"object"}
⟨11 unchanged words⟩ "minimum":0,"type":"integer"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"ordered_element_ids":{"description":"All element IDs currently ⟨30 unchanged words⟩ ,"required":["project_id","clip_index","ordered_element_ids","context","llm_model"],"type":"object"}
⟨30 unchanged words⟩ "minLength":1,"type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"dry_run":{"description":"Read-only tools still run; ⟨103 unchanged words⟩ "minLength":8,"type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"timeout_seconds":{"description":"Wall-clock budget, default ⟨117 unchanged words⟩ type":"integer"}},"required":["code","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project (guide) ⟨33 unchanged words⟩ ,"required":["project_id","voice_name","voice_engine","context","llm_model"],"type":"object"}
⟨14 unchanged words⟩ "minimum":0,"type":"integer"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project ID", ⟨36 unchanged words⟩ ,"required":["project_id","clip_index","split_time","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"workspaceId":{"description":"Workspace ID to switch to","type":"string"}},"required":["workspaceId","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"markdown":{"description":"Updated article content as ⟨13 unchanged words⟩ string"}},"required":["project_id","markdown","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"crop":{"additionalProperties":{},"description":" ⟨13 unchanged words⟩ :"string"},"type":"object"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"Project ID.","type ⟨59 unchanged words⟩ number"}},"required":["project_id","screenshot_id","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"fade_in":{"description":"Fade-in duration (seconds) ⟨37 unchanged words⟩ "minimum":0,"type":"number"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"loop":{"anyOf":[{"type":"boolean ⟨72 unchanged words⟩ number"}},"required":["project_id","music_id","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"Project ID","type ⟨527 unchanged words⟩ array"}},"required":["project_id","updates","context","llm_model"],"type":"object"}
⟨10 unchanged words⟩ to update.","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"description":{"description":"New description.","type":"string"},"files ⟨93 unchanged words⟩ :"object"},"type":"array"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"name":{"description":"New name.","type ⟨39 unchanged words⟩ type":"string"}},"required":["clueprint_id","context","llm_model"],"type":"object"}
before
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"propertyNames":{"type":"string"},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping; plus `placement` {y_anchor, top, center, bottom} — where the box landed and what your `y` meant, emitted only when the origin was implicit or resolves to 'center'. Compare placement.center with a container's centre line to verify centring: a mis-anchored label is the one layout error nothing else reports), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"},"updates":{"description":"Element updates — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_index":{"description":"Zero-based clip index. OPTIONAL — `element_id` is unique within the project, so the server finds the clip itself. Pass it only as a fast-path hint; a wrong one is corrected rather than rejected.","maximum":9007199254740991,"minimum":0,"type":"integer"},"element_id":{"description":"Element ID (from get_clip)","type":"string"},"element_type":{"description":"Required only when providing type_data. Use get_element_schema in 'update' mode to see fields.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"New time the element disappears, in seconds from the start of the clip.","minimum":0,"type":"number"},"fit_to_lines":{"description":"Text elements only. Target line count for the element. After this entry's regular updates land, the server widens (then font-shrinks if widen can't fit) the element so the rendered text wraps to at most N lines without overlapping its neighbours. Uses the same compute path as director's fit_text_layout.","maximum":9007199254740991,"minimum":1,"type":"integer"},"group":{"description":"Move this element into a named group, creating it if this clip has none by that name. Pass an empty string to pull it back out to the clip root. Omit to leave grouping unchanged. Grouping never moves anything — coordinates stay absolute.","type":"string"},"height":{"description":"New height, pixels (0 allowed). For text, the space you are giving it — the box resizes to the measured text. See layout.fits_height in the response.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Move this element in z-order, without rebuilding it. `front`/`back` are the ends of the group it is already in (the clip itself for an ungrouped element), so they never change what it belongs to — with one exception: for an element that is ALONE in its group they move the GROUP within its parent scope, because positioning a sole member inside its own group would be a no-op. `{before: element_id}` / `{after: element_id}` sit it next to that element or group and put it in THAT node's group — which is how you move something into or out of a group and position it in one call. Applied after `group` when both are passed."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Set keyframes for this element. Only the property tracks present in your entries are replaced — other tracks (color/rotation/typography/…) are preserved. Pass null OR an empty array to clear ALL keyframes. Same per-entry shape as add_elements; see get_element_schema for the keyframable properties of this element_type."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","maximum":9007199254740991,"minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"New label for the element.","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"save_as_component":{"description":"Animation elements only. Set true to save the current state as a reusable animation component (auto-named from the animation title), or pass a string to name it explicitly. Save runs AFTER any position/type_data updates land. Returns the component_id alongside the per-update result.","type":["boolean","string"]},"start_time":{"description":"New time the element appears, in seconds from the start of the clip.","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific updates. When provided, element_type is required. Get the schema via get_element_schema.","propertyNames":{"type":"string"},"type":"object"},"width":{"description":"New width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the wrap width and re-wraps the text.","minimum":0,"type":"number"},"x":{"description":"New X (canvas pixels). Same origin as add_elements: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right. Changing alignment in the same call re-anchors x to the new edge.","type":"number"},"y":{"description":"New Y (canvas pixels) — for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. See y_anchor.","type":"number"},"y_anchor":{"description":"TEXT only. ALWAYS send `y_anchor` ('top'|'center'|'bottom') when you set `y` on text, so the position states what you meant instead of inheriting the alignment origin. Omit `y` and it anchors to the element's CURRENT box, holding a centred label centred across a re-measure. Not stored — state it on every call. (Also accepted inside type_data; this one wins.)","enum":["top","center","bottom"],"type":"string"}},"required":["element_id"],"type":"object"},"minItems":1,"type":"array"}},"required":["project_id","updates","context","llm_model"],"type":"object"}after
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"propertyNames":{"type":"string"},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping; plus `placement` {y_anchor, top, center, bottom} — where the box landed and what your `y` meant, emitted only when the origin was implicit or resolves to 'center'. Compare placement.center with a container's centre line to verify centring: a mis-anchored label is the one layout error nothing else reports), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"},"updates":{"description":"Element updates — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_index":{"description":"Zero-based clip index. OPTIONAL — `element_id` is unique within the project, so the server finds the clip itself. Pass it only as a fast-path hint; a wrong one is corrected rather than rejected.","maximum":9007199254740991,"minimum":0,"type":"integer"},"element_id":{"description":"Element ID (from get_clip)","type":"string"},"element_type":{"description":"Required only when providing type_data. Use get_element_schema in 'update' mode to see fields.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"New time the element disappears, in seconds from the start of the clip.","minimum":0,"type":"number"},"fit_to_lines":{"description":"Text elements only. Target line count for the element. After this entry's regular updates land, the server widens (then font-shrinks if widen can't fit) the element so the rendered text wraps to at most N lines without overlapping its neighbours. Uses the same compute path as director's fit_text_layout.","maximum":9007199254740991,"minimum":1,"type":"integer"},"group":{"description":"Move this element into a named group, creating it if this clip has none by that name. Pass an empty string to pull it back out to the clip root. Omit to leave grouping unchanged. Grouping never moves anything — coordinates stay absolute.","type":"string"},"height":{"description":"New height, pixels (0 allowed). For text, the space you are giving it — the box resizes to the measured text. See layout.fits_height in the response.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Move this element in z-order, without rebuilding it. `front`/`back` are the ends of the group it is already in (the clip itself for an ungrouped element), so they never change what it belongs to — with one exception: for an element that is ALONE in its group they move the GROUP within its parent scope, because positioning a sole member inside its own group would be a no-op. `{before: element_id}` / `{after: element_id}` sit it next to that element or group and put it in THAT node's group — which is how you move something into or out of a group and position it in one call. Applied after `group` when both are passed."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Set keyframes for this element. Only the property tracks present in your entries are replaced — other tracks (color/rotation/typography/…) are preserved. Pass null OR an empty array to clear ALL keyframes. Same per-entry shape as add_elements; see get_element_schema for the keyframable properties of this element_type."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","maximum":9007199254740991,"minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"New label for the element.","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"save_as_component":{"description":"Animation elements only. Set true to save the current state as a reusable animation component (auto-named from the animation title), or pass a string to name it explicitly. Save runs AFTER any position/type_data updates land. Returns the component_id alongside the per-update result.","type":["boolean","string"]},"start_time":{"description":"New time the element appears, in seconds from the start of the clip.","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific updates. When provided, element_type is required. Get the schema via get_element_schema.","propertyNames":{"type":"string"},"type":"object"},"width":{"description":"New width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the wrap width and re-wraps the text.","minimum":0,"type":"number"},"x":{"description":"New X (canvas pixels). Same origin as add_elements: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right. Changing alignment in the same call re-anchors x to the new edge.","type":"number"},"y":{"description":"New Y (canvas pixels) — for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. See y_anchor.","type":"number"},"y_anchor":{"description":"TEXT only. ALWAYS send `y_anchor` ('top'|'center'|'bottom') when you set `y` on text, so the position states what you meant instead of inheriting the alignment origin. Omit `y` and it anchors to the element's CURRENT box, holding a centred label centred across a re-measure. Not stored — state it on every call. (Also accepted inside type_data; this one wins.)","enum":["top","center","bottom"],"type":"string"}},"required":["element_id"],"type":"object"},"minItems":1,"type":"array"}},"required":["project_id","updates"],"type":"object"}{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"items":{"description":"One entry per group ⟨537 unchanged words⟩ "minItems":1,"type":"array"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project ID", ⟨251 unchanged words⟩ array"}},"required":["project_id","items","context","llm_model"],"type":"object"}
⟨31 unchanged words⟩ [1-9]\\d*$","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"description":{"description":"New description","type":"string"},"folder_id ⟨11 unchanged words⟩ workspace root.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project ID to ⟨8 unchanged words⟩ type":"string"}},"required":["project_id","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"file_name":{"description":"File name with extension. ⟨54 unchanged words⟩ file from","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"request_hosted_upload":{"description":"If true, returns ⟨15 unchanged words⟩ under one token.","type":"boolean"}},"required":["context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"entries":{"description":"Voiceover entries — at ⟨153 unchanged words⟩ "minItems":1,"type":"array"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"Project ID","type":"string"}},"required":["project_id","entries","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"fade_in":{"description":"Fade-in duration in seconds. ⟨97 unchanged words⟩ "minimum":0,"type":"number"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"loop":{"description":"Loop the track if ⟨237 unchanged words⟩ number"}},"required":["project_id","type","context","llm_model"],"type":"object"}
⟨260 unchanged words⟩ :"object"},"type":"array"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"cuts":{"description":"kind='video' only: per-clip ⟨77 unchanged words⟩ ,"video"],"type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"mcp_upload_id":{"description":"kind='pptx' or 'video': ⟨98 unchanged words⟩ string"}},"required":["kind","project_id","context","llm_model"],"type":"object"}
⟨46 unchanged words⟩ "minLength":1,"type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"parent_comment_id":{"description":"Reply to this comment ⟨44 unchanged words⟩ string"}},"required":["project_id","comment_text","context","llm_model"],"type":"object"}
before
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"propertyNames":{"type":"string"},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"elements":{"description":"Elements to add — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_id":{"description":"Clip ID — accepted but unused; the clip is located by clip_index for all element types.","type":"string"},"clip_index":{"description":"Zero-based clip index. Required unless hoisted into `defaults`.","maximum":9007199254740991,"minimum":0,"type":"integer"},"component_id":{"description":"Insert a SAVED COMPONENT exactly as stored — no AI generation, no restyling. This is how you reuse a component from your workspace verbatim (find one with find(type='element_components')). Components are workspace-scoped — there is no community library for them. A component is a fragment of a clip — one or more elements, possibly grouped — so this inserts ALL of them and returns every new element_id plus the animation's parameter_schema. Customise afterwards with update_elements(type_data.parameter_values). Mutually exclusive with element_type/type_data. To instead GENERATE a new animation using a component as a style reference, use element_type='animation' with type_data.base_component_id.","minLength":1,"type":"string"},"element_type":{"description":"One of: text, callout, rectangle, arrow, blur, spotlight, zoom, image, video, animation. Omit when using component_id.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"Disappear at (seconds)","minimum":0,"type":"number"},"group":{"description":"Put this element in a named group. The name is the handle: items in ONE call sharing the same name land in the SAME group, and a later call with that name adds to it. Use it to keep a unit together (a card and its label, a stat and its caption) so the user can move or hide it as one thing. Grouping never moves anything — coordinates stay absolute. Animations and AI-generated images are created in the background and cannot be grouped as they are added; add them first, then set `group` via update_elements.","minLength":1,"type":"string"},"height":{"description":"Height, pixels (0 allowed). For text the box's height always follows the measured text, so this reads as the space you are giving it: the text is centred in that space and layout.fits_height says whether it fit. Use type_data.fit to shrink the font into it.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Where the element lands in z-order, so you don't need a follow-up reorder_elements. `front` (default) puts it on top of what's already on the clip; `back` puts it behind everything — what a backdrop panel or a wash needs. `{before: element_id}` / `{after: element_id}` place it next to a specific element or group and put it in THAT node's group, which is the only way to position inside a group. Passing `group` as well changes what `front`/`back` address — see the Z-order notes in this tool's description. Placement is relative to the clip AS IT STANDS when the item is applied, so several `back` items in one call end up in REVERSE input order: send one backdrop per call, or list them back-to-front."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Animation keyframes (≥2 entries). Each entry has a timestamp plus one or more keyframable properties (position/size in canvas pixels; rotation°, opacity 0-100, scale, colors as #rrggbb[aa], etc. in native units). Easing: per-entry `interpolation` + optional per-property `interpolation_overrides`. Call get_element_schema to see which properties this element_type can keyframe. null is accepted and means none."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","maximum":9007199254740991,"minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"Label","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"start_time":{"description":"Appear at (seconds)","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific fields. Use the get_element_schema tool first to discover the shape for a given element_type. Omit when using component_id.","propertyNames":{"type":"string"},"type":"object"},"width":{"description":"Width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the WRAP width and it is kept as sent: the text wraps to it and the box stays this wide. (Height differs — the box's height follows the measured text.)","minimum":0,"type":"number"},"x":{"description":"X position in canvas pixels. `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right (callout, rectangle, arrow, blur, spotlight, image and animation all use the left edge). For zoom, use type_data.centerX instead.","type":"number"},"y":{"description":"Y position in canvas pixels — for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. Pair it with y_anchor (below), always.","type":"number"},"y_anchor":{"description":"TEXT only. ALWAYS send `y_anchor` ('top'|'center'|'bottom') when you set `y` on text, so the position states what you meant instead of inheriting the alignment origin. To centre in a row, pill, chip, card or button: pass the container's centre line as `y` with y_anchor:'center'. The server centres the MEASURED box — you cannot compute that height, since wrapping and `fit` change it. A write-time DIRECTIVE: never stored, never returned by a read, so send it on every call whose `y` means the same thing. (Also accepted inside type_data — older spelling; this one wins.)","enum":["top","center","bottom"],"type":"string"}},"type":"object"},"minItems":1,"type":"array"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping; plus `placement` {y_anchor, top, center, bottom} — where the box landed and what your `y` meant, emitted only when the origin was implicit or resolves to 'center'. Compare placement.center with a container's centre line to verify centring: a mis-anchored label is the one layout error nothing else reports), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"}},"required":["project_id","elements"],"type":"object"}after
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"propertyNames":{"type":"string"},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"elements":{"description":"Elements to add — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_id":{"description":"Clip ID — accepted but unused; the clip is located by clip_index for all element types.","type":"string"},"clip_index":{"description":"Zero-based clip index. Required unless hoisted into `defaults`.","maximum":9007199254740991,"minimum":0,"type":"integer"},"component_id":{"description":"Insert a SAVED COMPONENT exactly as stored — no AI generation, no restyling. This is how you reuse a component from your workspace verbatim (find one with find(type='element_components')). Components are workspace-scoped — there is no community library for them. A component is a fragment of a clip — one or more elements, possibly grouped — so this inserts ALL of them and returns every new element_id plus the animation's parameter_schema. Customise afterwards with update_elements(type_data.parameter_values). Mutually exclusive with element_type/type_data. To instead GENERATE a new animation using a component as a style reference, use element_type='animation' with type_data.base_component_id.","minLength":1,"type":"string"},"element_type":{"description":"One of: text, callout, rectangle, arrow, blur, spotlight, zoom, image, video, animation. Omit when using component_id.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"Disappear at (seconds)","minimum":0,"type":"number"},"group":{"description":"Put this element in a named group. The name is the handle: items in ONE call sharing the same name land in the SAME group, and a later call with that name adds to it. Use it to keep a unit together (a card and its label, a stat and its caption) so the user can move or hide it as one thing. Grouping never moves anything — coordinates stay absolute. Animations and AI-generated images are created in the background and cannot be grouped as they are added; add them first, then set `group` via update_elements.","minLength":1,"type":"string"},"height":{"description":"Height, pixels (0 allowed). For text the box's height always follows the measured text, so this reads as the space you are giving it: the text is centred in that space and layout.fits_height says whether it fit. Use type_data.fit to shrink the font into it.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Where the element lands in z-order, so you don't need a follow-up reorder_elements. `front` (default) puts it on top of what's already on the clip; `back` puts it behind everything — what a backdrop panel or a wash needs. `{before: element_id}` / `{after: element_id}` place it next to a specific element or group and put it in THAT node's group, which is the only way to position inside a group. Passing `group` as well changes what `front`/`back` address — see the Z-order notes in this tool's description. Placement is relative to the clip AS IT STANDS when the item is applied, so several `back` items in one call end up in REVERSE input order: send one backdrop per call, or list them back-to-front."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Animation keyframes (≥2 entries). Each entry has a timestamp plus one or more keyframable properties (position/size in canvas pixels; rotation°, opacity 0-100, scale, colors as #rrggbb[aa], etc. in native units). Easing: per-entry `interpolation` + optional per-property `interpolation_overrides`. Call get_element_schema to see which properties this element_type can keyframe. null is accepted and means none."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","maximum":9007199254740991,"minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"Label","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"start_time":{"description":"Appear at (seconds)","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific fields. Use the get_element_schema tool first to discover the shape for a given element_type. Omit when using component_id.","propertyNames":{"type":"string"},"type":"object"},"width":{"description":"Width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the WRAP width and it is kept as sent: the text wraps to it and the box stays this wide. (Height differs — the box's height follows the measured text.)","minimum":0,"type":"number"},"x":{"description":"X position in canvas pixels. `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right (callout, rectangle, arrow, blur, spotlight, image and animation all use the left edge). For zoom, use type_data.centerX instead.","type":"number"},"y":{"description":"Y position in canvas pixels — for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. Pair it with y_anchor (below), always.","type":"number"},"y_anchor":{"description":"TEXT only. ALWAYS send `y_anchor` ('top'|'center'|'bottom') when you set `y` on text, so the position states what you meant instead of inheriting the alignment origin. To centre in a row, pill, chip, card or button: pass the container's centre line as `y` with y_anchor:'center'. The server centres the MEASURED box — you cannot compute that height, since wrapping and `fit` change it. A write-time DIRECTIVE: never stored, never returned by a read, so send it on every call whose `y` means the same thing. (Also accepted inside type_data — older spelling; this one wins.)","enum":["top","center","bottom"],"type":"string"}},"type":"object"},"minItems":1,"type":"array"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping; plus `placement` {y_anchor, top, center, bottom} — where the box landed and what your `y` meant, emitted only when the origin was implicit or resolves to 'center'. Compare placement.center with a container's centre line to verify centring: a mis-anchored label is the one layout error nothing else reports), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"}},"required":["project_id","elements","context","llm_model"],"type":"object"}⟨29 unchanged words⟩ "minimum":0,"type":"integer"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"occurrence":{"default":1,"description":"1-indexed ⟨77 unchanged words⟩ ,"required":["project_id","word","timestamp_seconds","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"file_url":{"description":"Public http(s) ⟨20 unchanged words⟩ private/loopback ranges.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"mcp_upload_id":{"description":"MCP upload ID from ⟨112 unchanged words⟩ type":"object"}},"required":["mode","context","llm_model"],"type":"object"}
⟨8 unchanged words⟩ to auto-sync.","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"Project ID.","type":"string"}},"required":["project_id","clip_id","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"upload_token":{"description":"The upload_token returned by ⟨3 unchanged words⟩ type":"string"}},"required":["upload_token","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"description":{"description":"Description of what this clueprint does","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"name":{"description":"Name of the clueprint ⟨34 unchanged words⟩ string"}},"required":["name","visibility","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"description":{"description":"Optional project description (max ~500 chars)"," ⟨22 unchanged words⟩ workspace root.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"title":{"description":"Project title","type":"string"}},"required":["context","llm_model"],"type":"object"}
⟨17 unchanged words⟩ if omitted.","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"source_clip_id":{"description":"ID of the clip ⟨34 unchanged words⟩ string"}},"required":["target_project_id","source_clip_id","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"folder_id":{"description":"Place the duplicate in ⟨18 unchanged words⟩ workspace root.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"new_title":{"description":"Override the title of ⟨11 unchanged words⟩ type":"string"}},"required":["source_project_id","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"texts":{"description":"Array of voiceover texts ⟨12 unchanged words⟩ type":"array"}},"required":["texts","context","llm_model"],"type":"object"}
⟨11 unchanged words⟩ default false)","type":"boolean"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"fps":{"anyOf":[{"const":24, ⟨11 unchanged words⟩ "Frames per second (default 30)"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project ID to ⟨14 unchanged words⟩ type":"string"}},"required":["project_id","context","llm_model"],"type":"object"}
⟨13 unchanged words⟩ e.g. 'blue'.","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"creator_id":{"description":"clueprints only: filter ⟨97 unchanged words⟩ "minimum":1,"type":"integer"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"max_duration":{"description":"stock videos + freesound ⟨236 unchanged words⟩ type":"string"}},"required":["type","context","llm_model"],"type":"object"}
⟨491 unchanged words⟩ element into.","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"end_time":{"description":"Disappear at (seconds) ⟨27 unchanged words⟩ ,"animation"],"type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"mcp_upload_id":{"description":"mcp_upload_id from the upload ⟨283 unchanged words⟩ ","x","y","width","height","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"Project ID","type":"string"}},"required":["project_id","context","llm_model"],"type":"object"}
⟨11 unchanged words⟩ "minimum":0,"type":"integer"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"element_ids":{"description":"WHICH element rows to ⟨41 unchanged words⟩ :"string"},"type":"array"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project ID", ⟨857 unchanged words⟩ array"}},"required":["project_id","clip_index","context","llm_model"],"type":"object"}
⟨8 unchanged words⟩ the clueprint.","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"file_paths":{"description":"include='files' only: relative ⟨38 unchanged words⟩ files","all"],"type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"}},"required":["clueprint_id","context","llm_model"],"type":"object"}
⟨20 unchanged words⟩ ones included.","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"include_resolved":{"description":"Include threads already marked ⟨16 unchanged words⟩ work outstanding.","type":"boolean"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project (guide) ID","type":"string"}},"required":["project_id","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"}},"required":["context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"element_type":{"description":"One of: text, ⟨106 unchanged words⟩ ,"json"],"type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"mode":{"default":"add","description": ⟨12 unchanged words⟩ type":"string"}},"required":["element_type","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project ID","type":"string"}},"required":["project_id","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"job_id":{"description":"From the run_script reply. ⟨20 unchanged words⟩ }|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"wait_seconds":{"description":"How long to wait ⟨17 unchanged words⟩ type":"integer"}},"required":["job_id","context","llm_model"],"type":"object"}
⟨7 unchanged words⟩ context ID","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"custom_instructions":{"default":"","description":" ⟨165 unchanged words⟩ : en)","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"preceding_clip_id":{"description":"Clip ID after which ⟨521 unchanged words⟩ :["guide_id","chat_id","preceding_clip_id","scenes","context","llm_model"],"type":"object"}
⟨13 unchanged words⟩ "minimum":0,"type":"integer"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project ID","type":"string"}},"required":["project_id","clip_index","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"Project ID","type ⟨42 unchanged words⟩ array"}},"required":["project_id","removals","context","llm_model"],"type":"object"}
⟨14 unchanged words⟩ "minimum":0,"type":"integer"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"element_id":{"description":"Element ID to remove ⟨33 unchanged words⟩ whole subtree.","type":"boolean"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"music_id":{"description":"Music/SFX track ID to ⟨27 unchanged words⟩ string"}},"required":["project_id","target","context","llm_model"],"type":"object"}
⟨11 unchanged words⟩ "minimum":0,"type":"integer"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"ordered_element_ids":{"description":"All element IDs currently ⟨30 unchanged words⟩ ,"required":["project_id","clip_index","ordered_element_ids","context","llm_model"],"type":"object"}
⟨30 unchanged words⟩ "minLength":1,"type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"dry_run":{"description":"Read-only tools still run; ⟨103 unchanged words⟩ "minLength":8,"type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"timeout_seconds":{"description":"Wall-clock budget, default ⟨117 unchanged words⟩ type":"integer"}},"required":["code","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project (guide) ⟨33 unchanged words⟩ ,"required":["project_id","voice_name","voice_engine","context","llm_model"],"type":"object"}
⟨14 unchanged words⟩ "minimum":0,"type":"integer"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project ID", ⟨36 unchanged words⟩ ,"required":["project_id","clip_index","split_time","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"workspaceId":{"description":"Workspace ID to switch to","type":"string"}},"required":["workspaceId","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"markdown":{"description":"Updated article content as ⟨13 unchanged words⟩ string"}},"required":["project_id","markdown","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"crop":{"additionalProperties":{},"description":" ⟨13 unchanged words⟩ :"string"},"type":"object"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"Project ID.","type ⟨59 unchanged words⟩ number"}},"required":["project_id","screenshot_id","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"fade_in":{"description":"Fade-in duration (seconds) ⟨37 unchanged words⟩ "minimum":0,"type":"number"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"loop":{"anyOf":[{"type":"boolean ⟨72 unchanged words⟩ number"}},"required":["project_id","music_id","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"Project ID","type ⟨527 unchanged words⟩ array"}},"required":["project_id","updates","context","llm_model"],"type":"object"}
⟨10 unchanged words⟩ to update.","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"description":{"description":"New description.","type":"string"},"files ⟨93 unchanged words⟩ :"object"},"type":"array"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"name":{"description":"New name.","type ⟨39 unchanged words⟩ type":"string"}},"required":["clueprint_id","context","llm_model"],"type":"object"}
before
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"propertyNames":{"type":"string"},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping; plus `placement` {y_anchor, top, center, bottom} — where the box landed and what your `y` meant, emitted only when the origin was implicit or resolves to 'center'. Compare placement.center with a container's centre line to verify centring: a mis-anchored label is the one layout error nothing else reports), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"},"updates":{"description":"Element updates — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_index":{"description":"Zero-based clip index. OPTIONAL — `element_id` is unique within the project, so the server finds the clip itself. Pass it only as a fast-path hint; a wrong one is corrected rather than rejected.","maximum":9007199254740991,"minimum":0,"type":"integer"},"element_id":{"description":"Element ID (from get_clip)","type":"string"},"element_type":{"description":"Required only when providing type_data. Use get_element_schema in 'update' mode to see fields.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"New time the element disappears, in seconds from the start of the clip.","minimum":0,"type":"number"},"fit_to_lines":{"description":"Text elements only. Target line count for the element. After this entry's regular updates land, the server widens (then font-shrinks if widen can't fit) the element so the rendered text wraps to at most N lines without overlapping its neighbours. Uses the same compute path as director's fit_text_layout.","maximum":9007199254740991,"minimum":1,"type":"integer"},"group":{"description":"Move this element into a named group, creating it if this clip has none by that name. Pass an empty string to pull it back out to the clip root. Omit to leave grouping unchanged. Grouping never moves anything — coordinates stay absolute.","type":"string"},"height":{"description":"New height, pixels (0 allowed). For text, the space you are giving it — the box resizes to the measured text. See layout.fits_height in the response.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Move this element in z-order, without rebuilding it. `front`/`back` are the ends of the group it is already in (the clip itself for an ungrouped element), so they never change what it belongs to — with one exception: for an element that is ALONE in its group they move the GROUP within its parent scope, because positioning a sole member inside its own group would be a no-op. `{before: element_id}` / `{after: element_id}` sit it next to that element or group and put it in THAT node's group — which is how you move something into or out of a group and position it in one call. Applied after `group` when both are passed."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Set keyframes for this element. Only the property tracks present in your entries are replaced — other tracks (color/rotation/typography/…) are preserved. Pass null OR an empty array to clear ALL keyframes. Same per-entry shape as add_elements; see get_element_schema for the keyframable properties of this element_type."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","maximum":9007199254740991,"minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"New label for the element.","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"save_as_component":{"description":"Animation elements only. Set true to save the current state as a reusable animation component (auto-named from the animation title), or pass a string to name it explicitly. Save runs AFTER any position/type_data updates land. Returns the component_id alongside the per-update result.","type":["boolean","string"]},"start_time":{"description":"New time the element appears, in seconds from the start of the clip.","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific updates. When provided, element_type is required. Get the schema via get_element_schema.","propertyNames":{"type":"string"},"type":"object"},"width":{"description":"New width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the wrap width and re-wraps the text.","minimum":0,"type":"number"},"x":{"description":"New X (canvas pixels). Same origin as add_elements: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right. Changing alignment in the same call re-anchors x to the new edge.","type":"number"},"y":{"description":"New Y (canvas pixels) — for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. See y_anchor.","type":"number"},"y_anchor":{"description":"TEXT only. ALWAYS send `y_anchor` ('top'|'center'|'bottom') when you set `y` on text, so the position states what you meant instead of inheriting the alignment origin. Omit `y` and it anchors to the element's CURRENT box, holding a centred label centred across a re-measure. Not stored — state it on every call. (Also accepted inside type_data; this one wins.)","enum":["top","center","bottom"],"type":"string"}},"required":["element_id"],"type":"object"},"minItems":1,"type":"array"}},"required":["project_id","updates"],"type":"object"}after
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"propertyNames":{"type":"string"},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping; plus `placement` {y_anchor, top, center, bottom} — where the box landed and what your `y` meant, emitted only when the origin was implicit or resolves to 'center'. Compare placement.center with a container's centre line to verify centring: a mis-anchored label is the one layout error nothing else reports), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"},"updates":{"description":"Element updates — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_index":{"description":"Zero-based clip index. OPTIONAL — `element_id` is unique within the project, so the server finds the clip itself. Pass it only as a fast-path hint; a wrong one is corrected rather than rejected.","maximum":9007199254740991,"minimum":0,"type":"integer"},"element_id":{"description":"Element ID (from get_clip)","type":"string"},"element_type":{"description":"Required only when providing type_data. Use get_element_schema in 'update' mode to see fields.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"New time the element disappears, in seconds from the start of the clip.","minimum":0,"type":"number"},"fit_to_lines":{"description":"Text elements only. Target line count for the element. After this entry's regular updates land, the server widens (then font-shrinks if widen can't fit) the element so the rendered text wraps to at most N lines without overlapping its neighbours. Uses the same compute path as director's fit_text_layout.","maximum":9007199254740991,"minimum":1,"type":"integer"},"group":{"description":"Move this element into a named group, creating it if this clip has none by that name. Pass an empty string to pull it back out to the clip root. Omit to leave grouping unchanged. Grouping never moves anything — coordinates stay absolute.","type":"string"},"height":{"description":"New height, pixels (0 allowed). For text, the space you are giving it — the box resizes to the measured text. See layout.fits_height in the response.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Move this element in z-order, without rebuilding it. `front`/`back` are the ends of the group it is already in (the clip itself for an ungrouped element), so they never change what it belongs to — with one exception: for an element that is ALONE in its group they move the GROUP within its parent scope, because positioning a sole member inside its own group would be a no-op. `{before: element_id}` / `{after: element_id}` sit it next to that element or group and put it in THAT node's group — which is how you move something into or out of a group and position it in one call. Applied after `group` when both are passed."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Set keyframes for this element. Only the property tracks present in your entries are replaced — other tracks (color/rotation/typography/…) are preserved. Pass null OR an empty array to clear ALL keyframes. Same per-entry shape as add_elements; see get_element_schema for the keyframable properties of this element_type."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","maximum":9007199254740991,"minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"New label for the element.","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"save_as_component":{"description":"Animation elements only. Set true to save the current state as a reusable animation component (auto-named from the animation title), or pass a string to name it explicitly. Save runs AFTER any position/type_data updates land. Returns the component_id alongside the per-update result.","type":["boolean","string"]},"start_time":{"description":"New time the element appears, in seconds from the start of the clip.","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific updates. When provided, element_type is required. Get the schema via get_element_schema.","propertyNames":{"type":"string"},"type":"object"},"width":{"description":"New width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the wrap width and re-wraps the text.","minimum":0,"type":"number"},"x":{"description":"New X (canvas pixels). Same origin as add_elements: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right. Changing alignment in the same call re-anchors x to the new edge.","type":"number"},"y":{"description":"New Y (canvas pixels) — for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. See y_anchor.","type":"number"},"y_anchor":{"description":"TEXT only. ALWAYS send `y_anchor` ('top'|'center'|'bottom') when you set `y` on text, so the position states what you meant instead of inheriting the alignment origin. Omit `y` and it anchors to the element's CURRENT box, holding a centred label centred across a re-measure. Not stored — state it on every call. (Also accepted inside type_data; this one wins.)","enum":["top","center","bottom"],"type":"string"}},"required":["element_id"],"type":"object"},"minItems":1,"type":"array"}},"required":["project_id","updates","context","llm_model"],"type":"object"}{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"items":{"description":"One entry per group ⟨537 unchanged words⟩ "minItems":1,"type":"array"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project ID", ⟨251 unchanged words⟩ array"}},"required":["project_id","items","context","llm_model"],"type":"object"}
⟨31 unchanged words⟩ [1-9]\\d*$","type":"string"},"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"description":{"description":"New description","type":"string"},"folder_id ⟨11 unchanged words⟩ workspace root.","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"The project ID to ⟨8 unchanged words⟩ type":"string"}},"required":["project_id","context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"file_name":{"description":"File name with extension. ⟨54 unchanged words⟩ file from","type":"string"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"request_hosted_upload":{"description":"If true, returns ⟨15 unchanged words⟩ under one token.","type":"boolean"}},"required":["context","llm_model"],"type":"object"}
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"context":{"description":"Explain in 15-25 words, in third person, why this tool is called and how it supports the user's goal. For analytics only. You MUST describe only the abstract purpose of the tool call. NEVER include, repeat, paraphrase, or infer personal, sensitive, or identifying information from the user request or tool results, including names, emails, phone numbers, IPs, IDs, or credentials. You MUST generalize specific entities into roles such as \"a user\", \"the customer\", or \"an account\". Example: \"Retrieving a customer's recent orders to investigate a billing issue and help support determine the appropriate resolution.\"","type":"string"},"conversation_id":{"description":"Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.","type":"string"},"entries":{"description":"Voiceover entries — at ⟨153 unchanged words⟩ "minItems":1,"type":"array"},"llm_model":{"description":"The exact model identifier you (the assistant) are running as, taken from your system prompt or environment (e.g. \"claude-opus-4-8\", \"gpt-5.2\"). Used for analytics only. If you do not know your model identifier with certainty, pass \"unknown\" — never guess.","type":"string"},"project_id":{"description":"Project ID","type":"string"}},"required":["project_id","entries","context","llm_model"],"type":"object"}
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"after_clip_id":{"description":" ⟨60 unchanged words⟩ no follow-up update_clips needed.","items":{"additionalProperties":false,"properties":{"background":{"additionalProperties":{}, ⟨4 unchanged words⟩ from find(type='backgrounds'), set at creation.","propertyNames":{"type":"string"},"type":"object"},"background_color":{"description":"Hex ⟨32 unchanged words⟩ ,"type":"string"},"transition":{"additionalProperties":false,"description":"Transition at the END of this ⟨147 unchanged words⟩ as a single clip.","items":{"additionalProperties":false,"properties":{"end_seconds":{"description":"Cut ⟨115 unchanged words⟩ Omit to insert all slides.","items":{"maximum":9007199254740991,"minimum":0,"type":"integer"}," ⟨40 unchanged words⟩
⟨33 unchanged words⟩ clip with an optional timestamp inside that clip. Pass `parent_comment_id` to REPLY to an existing comment rather than open a new thread. Read what is already there with `get_comments` first: answering a reviewer in their own thread is what makes the note resolvable, where a fresh top-level comment just adds noise.
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"clip_id":{"description":" ⟨36 unchanged words⟩ :"Comment text to post","minLength":1,"type":"string"},"parent_comment_id":{"description":"Reply to this comment instead of starting a new thread. Take the id from get_comments. Replying is how you answer feedback you have acted on — the clip pin and timestamp come from the parent, so leave clip_id/clip_timestamp off.","type":"string"},"project_id":{" ⟨12 unchanged words⟩
⟨286 unchanged words⟩ order in one call, grouped clips included. `group` + `insert_at: {before|after}` together: what the reference is relative to decides what gets positioned. Point at a node INSIDE the group and the ELEMENT takes that slot among its new siblings. Point at one OUTSIDE it and the GROUP takes that slot, with the element inside — which is how you put a backdrop pattern of N elements at a chosen depth as one hideable unit: hoist both into `defaults` and the first item seats the group, the rest just join it. The one refusal left is naming a reference outside a group that already exists somewhere else, since moving an established group is not what adding one element to it should do; seat the group where you want it in the call that CREATES it, because there is no reposition afterwards. `front`/`back` with a `group` follow the same principle: they address the GROUP only while the element is alone in it — the call that creates it — and once the group has other members they address the ELEMENT within the group's interior, so they will not move an established group either. Grouping: pass `group: "<name>"` to keep ⟨323 unchanged words⟩ (letterSpacing/lineHeight/padding* are also keyframable). Position origin:x/y is the top-left cornerforevery element typeTEXTexcept`y`text,iswherethehorizontalverticalalignmentCENTREmoveswhenitcentre-aligned(center-alignedandpositionsthebyTOPitsotherwise;centereverypoint,otherright-alignedtypebyusesitsthetop-right).top;For`x`text,issetthetype_data.y_anchorlefttoedge,sayexceptwhatTEXTywheremeansalignmentandpicksalignmentit:stopsleft→left,affectingcenter→centre,it.right→right. To centre a label in a row, ⟨80 unchanged words⟩
before
{"$schema":"http://json-schema.org/draft-07/schema#","additionalProperties":false,"properties":{"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"$ref":"#/properties/elements/items/properties/keyframes/anyOf/0/items"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"elements":{"description":"Elements to add — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_id":{"description":"Clip ID — accepted but unused; the clip is located by clip_index for all element types.","type":"string"},"clip_index":{"description":"Zero-based clip index. Required unless hoisted into `defaults`.","minimum":0,"type":"integer"},"component_id":{"description":"Insert a SAVED COMPONENT exactly as stored — no AI generation, no restyling. This is how you reuse a component from your workspace verbatim (find one with find(type='element_components')). Components are workspace-scoped — there is no community library for them. A component is a fragment of a clip — one or more elements, possibly grouped — so this inserts ALL of them and returns every new element_id plus the animation's parameter_schema. Customise afterwards with update_elements(type_data.parameter_values). Mutually exclusive with element_type/type_data. To instead GENERATE a new animation using a component as a style reference, use element_type='animation' with type_data.base_component_id.","minLength":1,"type":"string"},"element_type":{"description":"One of: text, callout, rectangle, arrow, blur, spotlight, zoom, image, video, animation. Omit when using component_id.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"Disappear at (seconds)","minimum":0,"type":"number"},"group":{"description":"Put this element in a named group. The name is the handle: items in ONE call sharing the same name land in the SAME group, and a later call with that name adds to it. Use it to keep a unit together (a card and its label, a stat and its caption) so the user can move or hide it as one thing. Grouping never moves anything — coordinates stay absolute. Animations and AI-generated images are created in the background and cannot be grouped as they are added; add them first, then set `group` via update_elements.","minLength":1,"type":"string"},"height":{"description":"Height, pixels (0 allowed). For text the box's height always follows the measured text, so this reads as the space you are giving it: the text is centred in that space and layout.fits_height says whether it fit. Use type_data.fit to shrink the font into it.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Where the element lands in z-order, so you don't need a follow-up reorder_elements. `front` (default) puts it on top of what's already on the clip; `back` puts it behind everything — what a backdrop panel or a wash needs. `{before: element_id}` / `{after: element_id}` place it next to a specific element or group and put it in THAT node's group, which is the only way to position inside a group. Placement is relative to the clip AS IT STANDS when the item is applied, so several `back` items in one call end up in REVERSE input order: send one backdrop per call, or list them back-to-front."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"]},"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"$ref":"#/properties/elements/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"$ref":"#/properties/elements/items/properties/keyframes/anyOf/0/items/properties/interpolation"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"]},"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Uses the SAME alignment-aware origin as the element's top-level x — left-aligned text uses top-left, center-aligned uses CENTER POINT, right-aligned uses top-right; all other types use top-left.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Origin matches positionX — center-aligned text positions by vertical center. y_anchor applies to the element's own y, not to keyframe values — keyframes use the alignment origin, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"$ref":"#/properties/elements/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"$ref":"#/properties/elements/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"$ref":"#/properties/elements/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"$ref":"#/properties/elements/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textStrokeColor":{"$ref":"#/properties/elements/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Animation keyframes (≥2 entries). Each entry has a timestamp plus one or more keyframable properties (position/size in canvas pixels; rotation°, opacity 0-100, scale, colors as #rrggbb[aa], etc. in native units). Easing: per-entry `interpolation` + optional per-property `interpolation_overrides`. Call get_element_schema to see which properties this element_type can keyframe. null is accepted and means none."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"Label","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"start_time":{"description":"Appear at (seconds)","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific fields. Use the get_element_schema tool first to discover the shape for a given element_type. Omit when using component_id.","type":"object"},"width":{"description":"Width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the WRAP width and it is kept as sent: the text wraps to it and the box stays this wide. (Height differs — the box's height follows the measured text.)","minimum":0,"type":"number"},"x":{"description":"X position in canvas pixels. ORIGIN is alignment-dependent: TEXT only — left-aligned uses top-left, center-aligned uses CENTER POINT, right-aligned uses top-right. All other element types (callout, rectangle, arrow, blur, spotlight, image, animation) use top-left. For zoom, use type_data.centerX instead.","type":"number"},"y":{"description":"Y position in canvas pixels. Which part of the element this refers to is set by type_data.y_anchor for text (top / center / bottom). To sit a label on a row, pill, chip, card or beside an icon, pass that container's centre line here with y_anchor:'center'.","type":"number"}},"type":"object"},"minItems":1,"type":"array"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"}},"required":["project_id","elements"],"type":"object"}{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"clip_id":{"description":" ⟨14 unchanged words⟩ clip index. Ignored if clip_id is provided.","maximum":9007199254740991,"minimum":0,"type":"integer"},"occurrence ⟨9 unchanged words⟩ repeats. Defaults to 1 (first occurrence).","maximum":9007199254740991,"minimum":1,"type":"integer"}, ⟨65 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"file_url":{"description":" ⟨62 unchanged words⟩ window in milliseconds. Default: whole file.","maximum":9007199254740991,"minimum":0,"type":"integer"},"range_start_ms ⟨5 unchanged words⟩ in milliseconds. Default 0 (beginning).","maximum":9007199254740991,"minimum":0,"type":"integer"},"silences_options":{"additionalProperties":false,"description":"Options for mode='silences'.","properties":{ ⟨5 unchanged words⟩ to report. Default 250ms.","exclusiveMinimum":0,"maximum":9007199254740991,"type":"integer"},"threshold_db":{"description" ⟨8 unchanged words⟩ },"type":"object"},"transcript_options":{"additionalProperties":false,"description":"Options for mode='transcript'.","properties": ⟨20 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"clip_id":{"description":" ⟨17 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"upload_token":{"description":" ⟨13 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"description":{"description":" ⟨54 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"description":{"description":" ⟨40 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"after_clip_id":{"description":" ⟨61 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"folder_id":{"description":" ⟨49 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"texts":{"description":" ⟨22 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"captions":{"description":" ⟨7 unchanged words⟩ ,"type":"boolean"},"fps":{"descriptionanyOf":[{"Frames per second (default 30)const":24,"enumtype":[24"number"},{"const":30,"type":"number"},{"const":60],"type":"number"}],"description":"Frames per second (default 30)"},"project_id":{"description":"The project ⟨22 unchanged words⟩
⟨32 unchanged words⟩ • backgrounds | voices | image_gen_style_packs | element_components (voices carry `gender`, `tags`, `accent`, `preview_url` and — where one has been written — a free-text `description` of tone and pace, which is the field actually worth choosing on. `tags` is a small closed vocabulary, so it groups voices rather than telling them apart. Choose deliberately: voiceover is the film's clock, and regenerating speech later rescales the clip and every element time and keyframe with it.) • images | videos | music | sfx ⟨521 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"color":{"description":" ⟨123 unchanged words⟩ audio: maximum clip length in seconds.","maximum":9007199254740991,"minimum":0,"type":"integer"},"min_duration ⟨3 unchanged words⟩ only: minimum clip length in seconds.","maximum":9007199254740991,"minimum":0,"type":"integer"},"mine_only ⟨23 unchanged words⟩ description":"Page (default 1) — projects/element_components/clueprints.","maximum":9007199254740991,"minimum":1,"type":"integer"}, ⟨178 unchanged words⟩
⟨12 unchanged words⟩ kind="image": text-to-image. Pass `prompt`. Optional: `animation_setting` (entry/exit — set it HERE, see below), `style_id` (from find type='image_gen_style_packs'), `reference_image_url` or `mcp_upload_id` ⟨43 unchanged words⟩ stable `element_id` and rendered in the background. Poll`get_clip``get_clip(select:['busy'])` — an EMPTY `busy` means the render has landed. (This previously said to watch thephantom`phantom`flagflag;drops`phantom`oncehasrenderingnevercompletesbeen a key get_clip returns, so there was nothing to poll.).Set presentation up front. `animation_setting` is applied to the element as it is created, so the image enters correctly the first time it renders. Doing it afterwards with `update_elements` means writing to the element that is still generating, which is the write most likely to be refused while the generation holds it. `group` is NOT accepted here, unlike `add_elements`: a generated element is built in the background, and the grouping would be overwritten when the render lands. Add it ungrouped, then call `update_elements` with `group` once it appears. Tip: use this tool whenever the user ⟨29 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#//json-schema.org/draft/2020-12/schema","additionalPropertiesproperties":false{"animation_setting":{"description":"Image only. Entry/exit animation, same shape as add_elements' type_data.animation_setting. The design guide asks AI images to enter with `masked_reveal` or a slow fade, so set it here rather than following up with update_elements — that follow-up targets the element while it is still generating. Ignored for kind='animation', which animates through its generated code.","properties":{"entry":{"description":"Entry (appear) animation phase.","properties":{"apply_to_unit":{"description":"Granularity. 'typewriter' is restricted to 'char'.","enum":["char","word","line","box"],"type":"string"},"direction":{"description":"Slide direction for this phase. Ignored by fade/scale/pop/typewriter; pass 'none' when the preset has no direction.","enum":["left","right","top","bottom","center_all","center_vertically","center_horizontally","none"],"type":"string"},"duration_per_unit_ms":{"description":"NAME IS MISLEADING — this is the TOTAL duration of the phase in ms (NOT per unit). The renderer splits it across all N units with ~50% overlap. For a perceptible per-unit rhythm scale UP with the unit count (e.g. typewriter on N chars ≈ N × 100–200 ms). For applyToUnit='box' it's just the phase duration. Omit it and you get 400ms, the same default the editor creates with.","exclusiveMinimum":0,"type":"number"},"preset":{"description":"Preset for this phase. 'typewriter' is char-only and has no direction.","enum":["slide","fade","pop","scale","masked_reveal","typewriter","none"],"type":"string"}},"type":"object"},"exit":{"description":"Exit (disappear) animation phase.","properties":{"apply_to_unit":{"description":"Granularity. 'typewriter' is restricted to 'char'.","enum":["char","word","line","box"],"type":"string"},"direction":{"description":"Slide direction for this phase. Ignored by fade/scale/pop/typewriter; pass 'none' when the preset has no direction.","enum":["left","right","top","bottom","center_all","center_vertically","center_horizontally","none"],"type":"string"},"duration_per_unit_ms":{"description":"NAME IS MISLEADING — this is the TOTAL duration of the phase in ms (NOT per unit). The renderer splits it across all N units with ~50% overlap. For a perceptible per-unit rhythm scale UP with the unit count (e.g. typewriter on N chars ≈ N × 100–200 ms). For applyToUnit='box' it's just the phase duration. Omit it and you get 400ms, the same default the editor creates with.","exclusiveMinimum":0,"type":"number"},"preset":{"description":"Preset for this phase. 'typewriter' is char-only and has no direction.","enum":["slide","fade","pop","scale","masked_reveal","typewriter","none"],"type":"string"}},"type":"object"}},"type":"object"},"background":{"anyOf":[{"type":"boolean"},{"enum":["0","1","true","false","yes","no"],"type":"string"}],"description":"Image only, and only honoured by `gpt-image-2.5-flare`, `gpt-image-2.5-sunburst`, `gpt-image-1` and `gpt-image-1.5` — every other model is always opaque and ignores this. Pass `false` for a cut-out with no background: icons, logos, stickers, anything meant to sit ON the composition rather than behind it. You MUST set `model` to one of those four in the same call; `gpt-image-2.5-flare` is the one to default to, since the older pair cannot frame wide."},"
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"project_id":{"description":" ⟨8 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"clip_index":{"description":"Zero-based clip index","maximum":9007199254740991,"minimum":0,"type":"integer"},"element_ids ⟨670 unchanged words⟩ , name, parent_id, bounds_px, anchor_px, keyframes), 'busy' (generations still writing to this clip, as `[{entity_path, job_type}]` — EMPTY means nothing is pending, which is how you know a voiceover or AI image has landed; it is the same lock that would refuse your write, so a non-empty list also tells you what not to touch yet). Each section returns the key it is ⟨35 unchanged words⟩ shapedata, imagedata, videodata, zoomdata, codedata,parent_id.\nparent_id. y_top is text-only and present ONLY when alignment is 'center' — the unambiguous TOP edge, which is exactly the case where `y` is NOT the top but the vertical CENTRE. To put that position back, send it as `y` with y_anchor:'top'.\n Examples: ['elements.x','elements.y','elements.width','elements.height'] to ⟨62 unchanged words⟩ {"enum":["elements","voiceover_words","groups","busy","elements.name","elements.element_type","elements.x","elements.y ⟨22 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"clueprint_id":{"description":" ⟨57 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#//json-schema.org/draft/2020-12/schema","properties":{},"type":"object"}
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"element_type":{"description":" ⟨134 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"project_id":{"description":" ⟨9 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"job_id":{"description":"From the run_script reply.","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","type":"string"},"wait_seconds":{ ⟨28 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"chat_id":{"description":" ⟨363 unchanged words⟩ it is assigned server-side.","items":{"additionalProperties":false,"properties":{"article_script":{"description":"Article ⟨53 unchanged words⟩ ,"type":"string"},"entry":{"additionalProperties":false,"description":"How this scene begins relative to ⟨81 unchanged words⟩ ","type":"boolean"},"exit":{"additionalProperties":false,"description":"How this scene ends.","properties ⟨184 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"clip_index":{"description":"Zero-based clip index to remove","maximum":9007199254740991,"minimum":0,"type":"integer"}, ⟨12 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"project_id":{"description":" ⟨18 unchanged words⟩ "clip_index":{"description":"Zero-based clip index","maximum":9007199254740991,"minimum":0,"type":"integer"}, ⟨22 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"clip_index":{"description":"Zero-based clip index (required for target='element')","maximum":9007199254740991,"minimum":0,"type":"integer"}, ⟨82 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"clip_index":{"description":"Zero-based clip index.","maximum":9007199254740991,"minimum":0,"type":"integer"}, ⟨42 unchanged words⟩
⟨309 unchanged words⟩ NOT use async/await, and there are no imports. `sleep(ms)` waits, synchronously like everything else here. Use it to poll a generation: `generate_media` and `voiceover_batch` return before their work lands, and `get_clip(select:['busy'])` says what is still being written. It is clamped to the script's remaining time, so a long sleep ends the run rather than overrunning it — and it holds one of the few concurrent script slots while it waits, so poll on the order of seconds, not milliseconds. const p = create_project({ title: "Launch ⟨545 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"code":{"description":" ⟨269 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"project_id":{"description":" ⟨42 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"clip_index":{"description":"Index of the clip to split","maximum":9007199254740991,"minimum":0,"type":"integer"}, ⟨47 unchanged words⟩
Switch the activeworkspaceworkspace. The choice sticks for thissession.client until you change it again — including across reconnects — so every later call operates in the new workspace. Use find(type='workspaces') to see available options.
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"workspaceId":{"description":" ⟨11 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"markdown":{"description":" ⟨23 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"crop":{"additionalProperties":{ ⟨7 unchanged words⟩ [w,h] } in fractional coords.","propertyNames":{"type":"string"},"type":"object"},"project_id":{"description":"Project ⟨38 unchanged words⟩ for annotations.","items":{"additionalProperties":{},"propertyNames":{"type":"string"},"type":"object"},"type":" ⟨19 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"fade_in":{"description":" ⟨46 unchanged words⟩ "type":"boolean"},{"enum":["true0","false1","1true","0false","yes","no"],"type":" ⟨70 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"project_id":{"description":" ⟨8 unchanged words⟩ per clip to update.","items":{"additionalProperties":false,"properties":{"background":{"additionalProperties":{},"description":"Full background object from find(type='backgrounds')","propertyNames":{"type":"string"},"type":"object"},"background_color":{"description":"Hex ⟨9 unchanged words⟩ ":{"description":"Zero-based clip index","maximum":9007199254740991,"minimum":0,"type":"integer"},"duration ⟨10 unchanged words⟩ "type":"boolean"},{"enum":["true0","false1","1true","0false","yes","no"],"type":" ⟨9 unchanged words⟩ :"Move clip to this zero-based index","maximum":9007199254740991,"minimum":0,"type":"integer"},"title ⟨5 unchanged words⟩ string"},"transition":{"anyOf":[{"additionalProperties":false,"properties":{"color":{"description":"Hex ⟨129 unchanged words⟩ one; null to remove"},"video_styling":{"additionalProperties":false,"description":"VIDEO CLIPS ONLY: reposition/resize the screen ⟨31 unchanged words⟩ properties":{"border":{"anyOf":[{"additionalProperties":false,"properties":{"borderWidth":{"description":"Border ⟨70 unchanged words⟩ clear."},"crop":{"anyOf":[{"additionalProperties":false,"properties":{"position":{"description":"[ ⟨176 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"clueprint_id":{"description":" ⟨21 unchanged words⟩ deletes. Empty/omitted for metadata-only updates.","items":{"additionalProperties":false,"properties":{"content":{"description":"Text ⟨130 unchanged words⟩
before
{"$schema":"http://json-schema.org/draft-07/schema#","additionalProperties":false,"properties":{"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"},"updates":{"description":"Element updates — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_index":{"description":"Zero-based clip index. OPTIONAL — `element_id` is unique within the project, so the server finds the clip itself. Pass it only as a fast-path hint; a wrong one is corrected rather than rejected.","minimum":0,"type":"integer"},"element_id":{"description":"Element ID (from get_clip)","type":"string"},"element_type":{"description":"Required only when providing type_data. Use get_element_schema in 'update' mode to see fields.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"New time the element disappears, in seconds from the start of the clip.","minimum":0,"type":"number"},"fit_to_lines":{"description":"Text elements only. Target line count for the element. After this entry's regular updates land, the server widens (then font-shrinks if widen can't fit) the element so the rendered text wraps to at most N lines without overlapping its neighbours. Uses the same compute path as director's fit_text_layout.","minimum":1,"type":"integer"},"group":{"description":"Move this element into a named group, creating it if this clip has none by that name. Pass an empty string to pull it back out to the clip root. Omit to leave grouping unchanged. Grouping never moves anything — coordinates stay absolute.","type":"string"},"height":{"description":"New height, pixels (0 allowed). For text, the space you are giving it — the box resizes to the measured text. See layout.fits_height in the response.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Move this element in z-order, without rebuilding it. `front`/`back` are the ends of the group it is already in (the clip itself for an ungrouped element), so they never change what it belongs to. `{before: element_id}` / `{after: element_id}` sit it next to that element or group and put it in THAT node's group — which is how you move something into or out of a group and position it in one call. Applied after `group` when both are passed."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"]},"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/interpolation"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"]},"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Uses the SAME alignment-aware origin as the element's top-level x — left-aligned text uses top-left, center-aligned uses CENTER POINT, right-aligned uses top-right; all other types use top-left.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Origin matches positionX — center-aligned text positions by vertical center. y_anchor applies to the element's own y, not to keyframe values — keyframes use the alignment origin, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textStrokeColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Set keyframes for this element. Only the property tracks present in your entries are replaced — other tracks (color/rotation/typography/…) are preserved. Pass null OR an empty array to clear ALL keyframes. Same per-entry shape as add_elements; see get_element_schema for the keyframable properties of this element_type."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"New label for the element.","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"save_as_component":{"description":"Animation elements only. Set true to save the current state as a reusable animation component (auto-named from the animation title), or pass a string to name it explicitly. Save runs AFTER any position/type_data updates land. Returns the component_id alongside the per-update result.","type":["boolean","string"]},"start_time":{"description":"New time the element appears, in seconds from the start of the clip.","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific updates. When provided, element_type is required. Get the schema via get_element_schema.","type":"object"},"width":{"description":"New width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the wrap width and re-wraps the text.","minimum":0,"type":"number"},"x":{"description":"New X (canvas pixels). ORIGIN follows the element's alignment: text uses top-left / CENTER / top-right per alignment, all other types use top-left. Changing alignment in the same call re-anchors x/y to the new origin.","type":"number"},"y":{"description":"New Y (canvas pixels). Takes type_data.y_anchor the same way add_elements does.","type":"number"}},"required":["element_id"],"type":"object"},"minItems":1,"type":"array"}},"required":["project_id","updates"],"type":"object"}after
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"propertyNames":{"type":"string"},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping; plus `placement` {y_anchor, top, center, bottom} — where the box landed and what your `y` meant, emitted only when the origin was implicit or resolves to 'center'. Compare placement.center with a container's centre line to verify centring: a mis-anchored label is the one layout error nothing else reports), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"},"updates":{"description":"Element updates — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_index":{"description":"Zero-based clip index. OPTIONAL — `element_id` is unique within the project, so the server finds the clip itself. Pass it only as a fast-path hint; a wrong one is corrected rather than rejected.","maximum":9007199254740991,"minimum":0,"type":"integer"},"element_id":{"description":"Element ID (from get_clip)","type":"string"},"element_type":{"description":"Required only when providing type_data. Use get_element_schema in 'update' mode to see fields.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"New time the element disappears, in seconds from the start of the clip.","minimum":0,"type":"number"},"fit_to_lines":{"description":"Text elements only. Target line count for the element. After this entry's regular updates land, the server widens (then font-shrinks if widen can't fit) the element so the rendered text wraps to at most N lines without overlapping its neighbours. Uses the same compute path as director's fit_text_layout.","maximum":9007199254740991,"minimum":1,"type":"integer"},"group":{"description":"Move this element into a named group, creating it if this clip has none by that name. Pass an empty string to pull it back out to the clip root. Omit to leave grouping unchanged. Grouping never moves anything — coordinates stay absolute.","type":"string"},"height":{"description":"New height, pixels (0 allowed). For text, the space you are giving it — the box resizes to the measured text. See layout.fits_height in the response.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Move this element in z-order, without rebuilding it. `front`/`back` are the ends of the group it is already in (the clip itself for an ungrouped element), so they never change what it belongs to — with one exception: for an element that is ALONE in its group they move the GROUP within its parent scope, because positioning a sole member inside its own group would be a no-op. `{before: element_id}` / `{after: element_id}` sit it next to that element or group and put it in THAT node's group — which is how you move something into or out of a group and position it in one call. Applied after `group` when both are passed."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Set keyframes for this element. Only the property tracks present in your entries are replaced — other tracks (color/rotation/typography/…) are preserved. Pass null OR an empty array to clear ALL keyframes. Same per-entry shape as add_elements; see get_element_schema for the keyframable properties of this element_type."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","maximum":9007199254740991,"minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"New label for the element.","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"save_as_component":{"description":"Animation elements only. Set true to save the current state as a reusable animation component (auto-named from the animation title), or pass a string to name it explicitly. Save runs AFTER any position/type_data updates land. Returns the component_id alongside the per-update result.","type":["boolean","string"]},"start_time":{"description":"New time the element appears, in seconds from the start of the clip.","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific updates. When provided, element_type is required. Get the schema via get_element_schema.","propertyNames":{"type":"string"},"type":"object"},"width":{"description":"New width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the wrap width and re-wraps the text.","minimum":0,"type":"number"},"x":{"description":"New X (canvas pixels). Same origin as add_elements: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right. Changing alignment in the same call re-anchors x to the new edge.","type":"number"},"y":{"description":"New Y (canvas pixels) — for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. See y_anchor.","type":"number"},"y_anchor":{"description":"TEXT only. ALWAYS send `y_anchor` ('top'|'center'|'bottom') when you set `y` on text, so the position states what you meant instead of inheriting the alignment origin. Omit `y` and it anchors to the element's CURRENT box, holding a centred label centred across a re-measure. Not stored — state it on every call. (Also accepted inside type_data; this one wins.)","enum":["top","center","bottom"],"type":"string"}},"required":["element_id"],"type":"object"},"minItems":1,"type":"array"}},"required":["project_id","updates"],"type":"object"}{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"items":{"description":" ⟨23 unchanged words⟩ the group's child bounding box.","items":[{false,"typemaxItems":2,"numberminItems"}:2,"prefixItems":[{"type":"number"}],{"maxItemstype":2,"minItemsnumber":2}],"type":"array"},"blur_px":{" ⟨13 unchanged words⟩ ":{"description":"Zero-based clip index","maximum":9007199254740991,"minimum":0,"type":"integer"},"collapsed ⟨93 unchanged words⟩ "rotateX","rotateY","opacity","blur"],"type":"string"},"required":["offsetX","offsetY","scaleX","scaleY","rotation","rotateX","rotateY","opacity","blur"],"type":"object"},"blur":{"description" ⟨38 unchanged words⟩ "string"},"interpolation_overrides":{"additionalProperties":{"$refenum":["#/properties/items/items/properties/keyframes/items/properties/interpolationlinear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. ⟨12 unchanged words⟩ "rotateX","rotateY","opacity","blur"],"type":"string"},"required":["offsetX","offsetY","scaleX","scaleY","rotation","rotateX","rotateY","opacity","blur"],"type":"object"},"offsetX":{"description" ⟨148 unchanged words⟩ Children's stored positions are untouched.","items":[{false,"typemaxItems":2,"numberminItems"}:2,"prefixItems":[{"type":"number"}],{"maxItemstype":2,"minItemsnumber":2}],"type":"array"},"opacity":{" ⟨43 unchanged words⟩ [x, y] in degrees","items":[{false,"typemaxItems":2,"numberminItems"}:2,"prefixItems":[{"type":"number"}],{"maxItemstype":2,"minItemsnumber":2}],"type":"array"},"rotation":{" ⟨19 unchanged words⟩ anchor (1 = unscaled)","items":[{false,"typemaxItems":2,"numberminItems"}:2,"prefixItems":[{"type":"number"}],{"maxItemstype":2,"minItemsnumber":2}],"type":"array"}},"required":[ ⟨119 unchanged words⟩ a lower bound on text that is alreadywrappingwrapping; plus `placement` {y_anchor, top, center, bottom} — where the box landed and what your `y` meant, emitted only when the origin was implicit or resolves to 'center'. Compare placement.center with a container's centre line to verify centring: a mis-anchored label is the one layout error nothing else reports), 'qa.codes' (audit counts and summary plus ⟨88 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"aspect_ratio":{"description":" ⟨71 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"file_name":{"description":" ⟨89 unchanged words⟩
{"$schema":"httphttps://json-schema.org/draft-07/schema#","additionalProperties//json-schema.org/draft/2020-12/schema":false,"properties":{"entries":{"description":"Voiceover entries — at least one.","items":{"anyOfoneOf":[{"additionalProperties":false,"properties":{"action":{"const":"set_text" ⟨14 unchanged words⟩ ":{"description":"Zero-based clip index","maximum":9007199254740991,"minimum":0,"type":"integer"},"text ⟨10 unchanged words⟩ "text"],"type":"object"},{"additionalProperties":false,"properties":{"action":{"const":"generate_speech" ⟨28 unchanged words⟩ Zero-based clip index. Give this or clip_id.","maximum":9007199254740991,"minimum":0,"type":"integer"}},"required":["action"],"type":"object"},{"additionalProperties":false,"properties":{"action":{"const":"set_and_generate" ⟨28 unchanged words⟩ :"Zero-based clip index. Give this or clip_id.","maximum":9007199254740991,"minimum":0,"type":"integer"}, ⟨28 unchanged words⟩
Get comments — Read the review comments on a project — the other half of `add_comment`, which could post but never read. Comments are how a human reviewer tells you what is wrong with a video: a note pinned to a clip and a moment inside it. Read them before an editing pass so you act on what was actually asked for, and read them again after a build if a reviewer has seen it. Threads come back nested: each top-level comment carries its `replies`. Reply into a thread with add_comment(parent_comment_id=...) rather than posting a new top-level note — a reviewer reading their own thread is how they see that th…
before
{"$schema":"http://json-schema.org/draft-07/schema#","additionalProperties":false,"properties":{"elements":{"description":"Elements to add — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_id":{"description":"Clip ID — accepted but unused; the clip is located by clip_index for all element types.","type":"string"},"clip_index":{"description":"Zero-based clip index","minimum":0,"type":"integer"},"component_id":{"description":"Insert a SAVED COMPONENT exactly as stored — no AI generation, no restyling. This is how you reuse a component from your workspace verbatim (find one with find(type='element_components')). Components are workspace-scoped — there is no community library for them. A component is a fragment of a clip — one or more elements, possibly grouped — so this inserts ALL of them and returns every new element_id plus the animation's parameter_schema. Customise afterwards with update_elements(type_data.parameter_values). Mutually exclusive with element_type/type_data. To instead GENERATE a new animation using a component as a style reference, use element_type='animation' with type_data.base_component_id.","minLength":1,"type":"string"},"element_type":{"description":"One of: text, callout, rectangle, arrow, blur, spotlight, zoom, image, video, animation. Omit when using component_id.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"Disappear at (seconds)","minimum":0,"type":"number"},"group":{"description":"Put this element in a named group. The name is the handle: items in ONE call sharing the same name land in the SAME group, and a later call with that name adds to it. Use it to keep a unit together (a card and its label, a stat and its caption) so the user can move or hide it as one thing. Grouping never moves anything — coordinates stay absolute. Animations and AI-generated images are created in the background and cannot be grouped as they are added; add them first, then set `group` via update_elements.","minLength":1,"type":"string"},"height":{"description":"Height, pixels (0 allowed)","minimum":0,"type":"number"},"keyframes":{"description":"Animation keyframes (≥2 entries). Each entry has a timestamp plus one or more keyframable properties (position/size in canvas pixels; rotation°, opacity 0-100, scale, colors as #rrggbb[aa], etc. in native units). Easing: per-entry `interpolation` + optional per-property `interpolation_overrides`. Call get_element_schema to see which properties this element_type can keyframe.","items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"]},"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"$ref":"#/properties/elements/items/properties/keyframes/items/properties/strokeColor","description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"$ref":"#/properties/elements/items/properties/keyframes/items/properties/interpolation"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"]},"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Uses the SAME alignment-aware origin as the element's top-level x — left-aligned text uses top-left, center-aligned uses CENTER POINT, right-aligned uses top-right; all other types use top-left.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Origin matches positionX — center-aligned text positions by vertical center.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"$ref":"#/properties/elements/items/properties/keyframes/items/properties/strokeColor","description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"$ref":"#/properties/elements/items/properties/keyframes/items/properties/strokeColor","description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"$ref":"#/properties/elements/items/properties/keyframes/items/properties/strokeColor","description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textStrokeColor":{"$ref":"#/properties/elements/items/properties/keyframes/items/properties/strokeColor","description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},"name":{"description":"Label","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"start_time":{"description":"Appear at (seconds)","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific fields. Use the get_element_schema tool first to discover the shape for a given element_type. Omit when using component_id.","type":"object"},"width":{"description":"Width, pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"},"x":{"description":"X position in canvas pixels. ORIGIN is alignment-dependent: TEXT only — left-aligned uses top-left, center-aligned uses CENTER POINT, right-aligned uses top-right. All other element types (callout, rectangle, arrow, blur, spotlight, image, animation) use top-left. For zoom, use type_data.center_x instead.","type":"number"},"y":{"description":"Y position in canvas pixels. ORIGIN matches x: center-aligned text positions by its vertical center. All other types and alignments use the top edge.","type":"number"}},"required":["clip_index"],"type":"object"},"minItems":1,"type":"array"},"project_id":{"description":"Project ID","type":"string"}},"required":["project_id","elements"],"type":"object"}after
{"$schema":"http://json-schema.org/draft-07/schema#","additionalProperties":false,"properties":{"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"$ref":"#/properties/elements/items/properties/keyframes/anyOf/0/items"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"elements":{"description":"Elements to add — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_id":{"description":"Clip ID — accepted but unused; the clip is located by clip_index for all element types.","type":"string"},"clip_index":{"description":"Zero-based clip index. Required unless hoisted into `defaults`.","minimum":0,"type":"integer"},"component_id":{"description":"Insert a SAVED COMPONENT exactly as stored — no AI generation, no restyling. This is how you reuse a component from your workspace verbatim (find one with find(type='element_components')). Components are workspace-scoped — there is no community library for them. A component is a fragment of a clip — one or more elements, possibly grouped — so this inserts ALL of them and returns every new element_id plus the animation's parameter_schema. Customise afterwards with update_elements(type_data.parameter_values). Mutually exclusive with element_type/type_data. To instead GENERATE a new animation using a component as a style reference, use element_type='animation' with type_data.base_component_id.","minLength":1,"type":"string"},"element_type":{"description":"One of: text, callout, rectangle, arrow, blur, spotlight, zoom, image, video, animation. Omit when using component_id.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"Disappear at (seconds)","minimum":0,"type":"number"},"group":{"description":"Put this element in a named group. The name is the handle: items in ONE call sharing the same name land in the SAME group, and a later call with that name adds to it. Use it to keep a unit together (a card and its label, a stat and its caption) so the user can move or hide it as one thing. Grouping never moves anything — coordinates stay absolute. Animations and AI-generated images are created in the background and cannot be grouped as they are added; add them first, then set `group` via update_elements.","minLength":1,"type":"string"},"height":{"description":"Height, pixels (0 allowed). For text the box's height always follows the measured text, so this reads as the space you are giving it: the text is centred in that space and layout.fits_height says whether it fit. Use type_data.fit to shrink the font into it.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Where the element lands in z-order, so you don't need a follow-up reorder_elements. `front` (default) puts it on top of what's already on the clip; `back` puts it behind everything — what a backdrop panel or a wash needs. `{before: element_id}` / `{after: element_id}` place it next to a specific element or group and put it in THAT node's group, which is the only way to position inside a group. Placement is relative to the clip AS IT STANDS when the item is applied, so several `back` items in one call end up in REVERSE input order: send one backdrop per call, or list them back-to-front."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"]},"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"$ref":"#/properties/elements/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"$ref":"#/properties/elements/items/properties/keyframes/anyOf/0/items/properties/interpolation"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"]},"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Uses the SAME alignment-aware origin as the element's top-level x — left-aligned text uses top-left, center-aligned uses CENTER POINT, right-aligned uses top-right; all other types use top-left.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Origin matches positionX — center-aligned text positions by vertical center. y_anchor applies to the element's own y, not to keyframe values — keyframes use the alignment origin, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"$ref":"#/properties/elements/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"$ref":"#/properties/elements/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"$ref":"#/properties/elements/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"$ref":"#/properties/elements/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textStrokeColor":{"$ref":"#/properties/elements/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Animation keyframes (≥2 entries). Each entry has a timestamp plus one or more keyframable properties (position/size in canvas pixels; rotation°, opacity 0-100, scale, colors as #rrggbb[aa], etc. in native units). Easing: per-entry `interpolation` + optional per-property `interpolation_overrides`. Call get_element_schema to see which properties this element_type can keyframe. null is accepted and means none."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"Label","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"start_time":{"description":"Appear at (seconds)","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific fields. Use the get_element_schema tool first to discover the shape for a given element_type. Omit when using component_id.","type":"object"},"width":{"description":"Width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the WRAP width and it is kept as sent: the text wraps to it and the box stays this wide. (Height differs — the box's height follows the measured text.)","minimum":0,"type":"number"},"x":{"description":"X position in canvas pixels. ORIGIN is alignment-dependent: TEXT only — left-aligned uses top-left, center-aligned uses CENTER POINT, right-aligned uses top-right. All other element types (callout, rectangle, arrow, blur, spotlight, image, animation) use top-left. For zoom, use type_data.centerX instead.","type":"number"},"y":{"description":"Y position in canvas pixels. Which part of the element this refers to is set by type_data.y_anchor for text (top / center / bottom). To sit a label on a row, pill, chip, card or beside an icon, pass that container's centre line here with y_anchor:'center'.","type":"number"}},"type":"object"},"minItems":1,"type":"array"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"}},"required":["project_id","elements"],"type":"object"}Analyze an audio file. Modes: - transcript: Speech-to-text with word-leveltimestamps.timestampsWorks—onreturnsBOTH`{language,speechfull_text,andwords}`sungwherelyricseach—wordroutesisto`{word,astart_ms,music-awareend_ms,providerpunctuated_word}`.when⚠contentMILLISECONDSishere;detectedget_clip'sas`voiceover_words`music.givesUsethetosamegetwords`words[in SECONDS as `{wword,start_msstart,end_msend}]``. Use for caption timing, script editing, or word-level visual sync.NoteWorks on narration and on sung lyrics:features.has_speechthemayproviderreportisfalsepickedonfrompurethemusiccontent,whileandtranscriptifstillitsuccessfullyreturnsextractsnothinglyrics.the other one is tried automatically. So an empty `words` means the audio genuinely has no intelligible speech, not that the wrong engine ran. - silences:listreturnsof`{silences,silenttotal_silence_ms}`,regionseachwithspanstart/end/peak.`{start_ms, end_ms, peak_db}`. Use for auto-trim, smart-split, or pause-aware editing. - beats:temporeturns(bpm)`{tempo_bpm,+beats_ms}`beat—positionsbeats_msforismusicantracks.arrayReturnsofemptyMILLISECOND positions. Empty for non-music. Use to align animations/zooms to a beat. - features:durationreturns `{duration_ms,peak/rms/lufspeak_db, rms_db, lufs, has_music, has_speech}` — loudness plus two INDEPENDENT hints — `has_music` (beats hold a steady period) and `has_speech` (there is audible content above a loudness floor; it is not a speech detector). Both can be true at once,speech-vs-musice.g.heuristic.narration over a music bed. Cheap dispatcher — call once todecide whichpickothera modeto use, or for duration_ms togetpassduration_msasforadd_audio'ssource_durationsource_duration.whenWhencallingtheadd_audio.words matter, trust transcript rather than these flags. Source: provide exactly one of mcp_upload_id (from ⟨26 unchanged words⟩
⟨90 unchanged words⟩ ,'elements.end_time'] → a timing pass select: ['words''voiceover_words']→ word timings only, to sync visuals to narration select: ['elements.textdata','words''voiceover_words']→ rewrite copy against the VO select: ['elements'] → whole element rows, no words select: ['groups'] → group rows only, to get a group_id for update_groups select: [] → no JSON at all (pair with ⟨34 unchanged words⟩ JSON, `render` produces a PNG. render: {}→ the frame at t=0 render: {timestamptimestamps: 2.5 }→ the frame 2.5s into the clip render: { timestamps: [0.5, 2, 4] } → those three moments as ONE labelled grid render: { timestamps: [...], layout:'separate'} → the same moments as full-size images (~4x the tokens) render: { save: true }→ also uploads thePNGframe and returns presigned_url render: { max_width: 1280 } → a sharper frame when you must read small print select: [], render: {}→ the PNG alone, no JSON select: ['elements'], render: {}→ element rows AND the frame A single frame renders 960px wide by default — legible for this design system and about half the tokens of a 1280 frame. A GRID defaults to 1280, because that is the width of the whole grid and a third of 960 would leave each cell unreadable. Either way `max_width` caps the image you get back; raise it only to read genuinely small print. Omitting `render` renders nothing.`timestamp``timestamps`, `layout` and `save` live inside it because they only ⟨48 unchanged words⟩ Element shape: universal wrapper fields (id,geoelement_type, name, x, y, width, height, ⟨61 unchanged words⟩ arg; null if none) and `voiceover_words` (per-wordtimestamps;timestamps, `{word, start, end, punctuated_word}` with start/end in SECONDS; null on clips with no transcription).
⟨73 unchanged words⟩ ":{"additionalProperties":false,"description":"RenderathePNGclipofastheanframe.image. Presence of this object IS the request to ⟨31 unchanged words⟩ (smallest read). CHECKING YOUR WORK: passa`timestamps` with SEVERAL mid-clip`timestamp`moments, not the t=0 default — text and ⟨18 unchanged words⟩ and a correct edit renders as an emptyframe.frame,Shapeswhile shapes have no entry animation and do show att=0,t=0.whichThat
Return theJSONschema for an element type'stype_data.type_data, as TypeScript declarations. Call this before add_elements / update_elements when you don't already know the field shape for the element_type you're placing. Pass element_type='group' for the GROUP surface instead — what a group is, how its pivot works, and the nine properties it can animate.
⟨14 unchanged words⟩ blur, spotlight, zoom, image, video, animation, group","enum":["text","callout"," ⟨4 unchanged words⟩ ","zoom","image","video","animation","group"],"type":"string"},"fields":{"description":"Return only these fields, with their prose — text is 15KB whole, ~300 bytes for two. Omit while learning the type: only the full schema carries the keyframable list and the keyframe rules.","items":{"type":"string"},"type":"array"},"format":{"description":"Defaults to 'declarations' — TypeScript-style, which is the better READ. Inside run_script it defaults to 'json' instead, because a script indexes into the structure and the declaration text would arrive newline-escaped. Set it explicitly to override either default.","enum":["declarations","json"],"type":"string"},"mode": ⟨21 unchanged words⟩
⟨23 unchanged words⟩ domain: the individual element) — same asadd_elements/update_elements.update_elements. Each removal is a granular element_remove patchmergedscopedundertoathatper-guideelementlockid, and the whole batch lands in ONE save. Fan out across subagentsfreely;astwolong as they touch DIFFERENT element ids. Two concurrent edits to the SAME element idserialize.do not last-write-win — the later one is REJECTED and nothing is written; re-read and re-apply. Do NOT run concurrently with whole-clip/whole-project mutations on ⟨25 unchanged words⟩
Remove element, group or audio track
Remove an element, a group or an audio track from a project. - target="element": removes an element from a clip (requires clip_index + element_id) - target="group": removes a GROUP node (requires clip_index + group_id). By default its children survive — they rise to the removed group's own parent, which for a top-level group is the clip root. Pass keep_children=false to delete the whole subtree instead, every nested group and every element inside it. - target="audio": removes a music/SFX track ⟨51 unchanged words⟩
⟨25 unchanged words⟩ for target='element')","type":"string"},"group_id":{"description":"Group ID to remove (required for target='group')","type":"string"},"keep_children":{"description":"target='group' only. Omit/true ungroups: children rise to the removed group's own parent. false deletes the whole subtree.","type":"boolean"},"music_id":{"description":"Music/SFX track ID to ⟨14 unchanged words⟩ ":{"description":"What to remove: 'element', 'group' or 'audio'","enum":["element","group","audio"],"type":"string"}} ⟨5 unchanged words⟩
⟨3 unchanged words⟩ false,"readOnlyHint":false,"title":"Remove element, group or audio track"}
⟨46 unchanged words⟩ drop an element. Get the current list viaget_clip.get_clip — the order it returns is exactly the order this takes. Works on grouped clips. Elements keep their group; moving a group's members together moves the whole group relative to everything else. The one thing a group cannot do is render in two pieces, so an order that puts a non-member BETWEEN two members of the same group is rejected and names the group — put the group's elements next to each other, or take the element out of the group first with update_elements(group).
⟨12 unchanged words⟩ call. Replaces the per-element tool — always batch. SEND LESS, GET BACK LESS. `defaults` carries what every item shares (clip_index, element_type, and type_data entries like font_setting or alignment) so you send it once instead of per item; each item overrides any key it sets. `returning` declares what comes back, the way `select` shapes a get_clip read — omit it for a sensible default, or pass [] for just the counts. defaults: { clip_index: 1, element_type: 'text', type_data: { font_setting: {...} } } returning: [] → { total, succeeded, failed } only returning: ['layout'] → + the measured text fit per element returning: ['qa.codes'] → + the clip audit with prose stripped from non-errors (any returning value → error findings regardless; only warnings are opt-in) Each item can update position/timing fields alone, or ⟨21 unchanged words⟩ are applied in order; partial success is fine. You do NOT need clip_index here — an element_id is unique within the project, so the server locates the clip itself. Pass it only as a hint; a wrong one is corrected rather than rejected. Grouping: pass `group: "<name>"` to move ⟨118 unchanged words⟩ same as add_elements — fan out across subagentsfreely;asthelongserverasmergestheyper-elementtouchunderDIFFERENTaelementper-guideidslock.— those carry different entity paths and both land. Two concurrent edits to thesameSAME element idserialize.do NOT merge and do NOT last-write-win: the later one is REJECTED ('changed since this edit was based on'), nothing is written, and you must re-read and re-apply. Do NOT run concurrently with whole-clip/whole-project mutations on ⟨86 unchanged words⟩
before
{"$schema":"http://json-schema.org/draft-07/schema#","additionalProperties":false,"properties":{"project_id":{"description":"Project ID","type":"string"},"updates":{"description":"Element updates — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_index":{"description":"Zero-based clip index","minimum":0,"type":"integer"},"element_id":{"description":"Element ID (from get_clip)","type":"string"},"element_type":{"description":"Required only when providing type_data. Use get_element_schema in 'update' mode to see fields.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"New time the element disappears, in seconds from the start of the clip.","minimum":0,"type":"number"},"fit_to_lines":{"description":"Text elements only. Target line count for the element. After this entry's regular updates land, the server widens (then font-shrinks if widen can't fit) the element so the rendered text wraps to at most N lines without overlapping its neighbours. Uses the same compute path as director's fit_text_layout.","minimum":1,"type":"integer"},"group":{"description":"Move this element into a named group, creating it if this clip has none by that name. Pass an empty string to pull it back out to the clip root. Omit to leave grouping unchanged. Grouping never moves anything — coordinates stay absolute.","type":"string"},"height":{"description":"New height, pixels (0 allowed)","minimum":0,"type":"number"},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"]},"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/strokeColor","description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/interpolation"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"]},"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Uses the SAME alignment-aware origin as the element's top-level x — left-aligned text uses top-left, center-aligned uses CENTER POINT, right-aligned uses top-right; all other types use top-left.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Origin matches positionX — center-aligned text positions by vertical center.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/strokeColor","description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/strokeColor","description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/strokeColor","description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textStrokeColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/strokeColor","description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Set keyframes for this element. Only the property tracks present in your entries are replaced — other tracks (color/rotation/typography/…) are preserved. Pass null OR an empty array to clear ALL keyframes. Same per-entry shape as add_elements; see get_element_schema for the keyframable properties of this element_type."},"name":{"description":"New label for the element.","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"save_as_component":{"description":"Animation elements only. Set true to save the current state as a reusable animation component (auto-named from the animation title), or pass a string to name it explicitly. Save runs AFTER any position/type_data updates land. Returns the component_id alongside the per-update result.","type":["boolean","string"]},"start_time":{"description":"New time the element appears, in seconds from the start of the clip.","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific updates. When provided, element_type is required. Get the schema via get_element_schema.","type":"object"},"width":{"description":"New width, pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"},"x":{"description":"New X (canvas pixels). ORIGIN follows the element's alignment: text uses top-left / CENTER / top-right per alignment, all other types use top-left. Changing alignment in the same call re-anchors x/y to the new origin.","type":"number"},"y":{"description":"New Y (canvas pixels). ORIGIN matches x — center-aligned text positions by vertical center.","type":"number"}},"required":["clip_index","element_id"],"type":"object"},"minItems":1,"type":"array"}},"required":["project_id","updates"],"type":"object"}⟨34 unchanged words⟩ generate_speech" — async TTS for a clip (clip_id;returns immediately) - "set_and_generate" — set text and kick off TTS in one entry (clip_index+clip_id +text) Address a clip the same way as everywhere else: `clip_index`. Pass `clip_id` instead if you already have it — it survives clips being inserted or reordered mid-build — but you never need both; whichever you omit is looked up once for the whole call. Entries within one call are applied in order. ⟨19 unchanged words⟩ async per clip after. IMPORTANT — generating speechretimesRESCALES the whole clip, it does not clamp it: when audio is generated (generate_speech / set_and_generate), the clip's duration is reset to the spoken audio length, and then EVERY element on that clip is retimed proportionally by (auto-fitnew duration ÷ old duration).Anystart_time,elementend_timestart/endandtimeseveryyoukeyframeauthoredtimestampagainstareamultipliedplannedbydurationthatthenfactor.playNothingagainstis merely truncated — on a 6s clip thatnewbecomeslength1.02s,andanelementanimationtimeyoupastplacedtheataudio[0,end1.6]isendscut.upSoatsize[0,timings0.27].againstZoom elements whose window falls under thespokenminimumlength:afterestimatescalingitareupDROPPEDfrontentirely.withGenerationestimate_durationis(orasync, so this lands AFTER this call has already returned success. So: generate speech BEFORE placing time-sensitive elements), or size them against estimate_duration first — thenreadre-read the clipbackandaftercheckgenerationwhattoyourconfirmelementsitsactuallyfinalbecame, not just the clip duration. Concurrency: parallel-safe (conflict domain: a clip's ⟨30 unchanged words⟩ it's safe to run alongside element edits. Two concurrent edits to the SAME clip's voiceoverserialize.do not last-write-win — both claim that clip's voiceover path, so the later one is REJECTED and nothing is written; re-read and re-apply. Do NOT run concurrently with whole-clip/whole-project mutations on ⟨13 unchanged words⟩
⟨77 unchanged words⟩ string"},"clip_id":{"description":"ClipIDID. Give this or clip_index.","minLength":1,"type":"string"},"clip_index":{"description":"Zero-based clip index. Give this or clip_id.","minimum":0,"type":"integer"}},"required":["action","clip_id"],"type":"object"},{"additionalProperties": ⟨18 unchanged words⟩ string"},"clip_id":{"description":"ClipIDID. Give this or clip_index.","minLength":1,"type":"string"},"clip_index":{"description":"Zero-based clipindexindex. Give this or clip_id.","minimum":0,"type":"integer"} ⟨6 unchanged words⟩ type":"string"}},"required":["action","clip_index","clip_id","text"],"type":"object"}] ⟨15 unchanged words⟩
Get a script job's result — Fetch the outcome of a run_script that did not finish inline. Waits for it rather than returning immediately, so one call is usually enough — set `wait_seconds` to how long you are willing to hold. Returns { job_id, status, result?, error? }. `status` is processing, completed or failed; `result` carries the same { ok, result, stdout, ops } the inline reply would have. The script runs to completion whether or not you poll.
Run a script that calls these tools — Write a JavaScript program that calls this server's other tools. You have the whole language: loops, arithmetic, functions, conditionals, and values carried from one call into the next — all running next to the tools instead of across the conversation. Use it whenever code says the thing more directly than a sequence of separate calls would, which is often. COMPUTE, don't hand-write. Anything you would otherwise work out in your head and type as literals is better computed here — eased keyframe tracks, staggered start times, grid coordinates, derived palettes, positions from measured text wi…
Update groups — Transform, keyframe, rename, nest or lock GROUPS. A group moves, scales, rotates and fades as ONE unit, without touching any child's stored position — which is how you get a camera push instead of building the target layout twice and crossfading it. Address by `group_id`, never by name. Ids come from get_clip, or from the `group_id` that add_elements/update_elements hands back when you pass `group`. An unknown id is an error, never a silent create. There is no add_groups: a group is CREATED by putting elements in one — `add_elements(group: "hero-card")` — which also returns its id. That is …
after
{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"maximum":9007199254740991,"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"propertyNames":{"type":"string"},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"elements":{"description":"Elements to add — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_id":{"description":"Clip ID — accepted but unused; the clip is located by clip_index for all element types.","type":"string"},"clip_index":{"description":"Zero-based clip index. Required unless hoisted into `defaults`.","maximum":9007199254740991,"minimum":0,"type":"integer"},"component_id":{"description":"Insert a SAVED COMPONENT exactly as stored — no AI generation, no restyling. This is how you reuse a component from your workspace verbatim (find one with find(type='element_components')). Components are workspace-scoped — there is no community library for them. A component is a fragment of a clip — one or more elements, possibly grouped — so this inserts ALL of them and returns every new element_id plus the animation's parameter_schema. Customise afterwards with update_elements(type_data.parameter_values). Mutually exclusive with element_type/type_data. To instead GENERATE a new animation using a component as a style reference, use element_type='animation' with type_data.base_component_id.","minLength":1,"type":"string"},"element_type":{"description":"One of: text, callout, rectangle, arrow, blur, spotlight, zoom, image, video, animation. Omit when using component_id.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"Disappear at (seconds)","minimum":0,"type":"number"},"group":{"description":"Put this element in a named group. The name is the handle: items in ONE call sharing the same name land in the SAME group, and a later call with that name adds to it. Use it to keep a unit together (a card and its label, a stat and its caption) so the user can move or hide it as one thing. Grouping never moves anything — coordinates stay absolute. Animations and AI-generated images are created in the background and cannot be grouped as they are added; add them first, then set `group` via update_elements.","minLength":1,"type":"string"},"height":{"description":"Height, pixels (0 allowed). For text the box's height always follows the measured text, so this reads as the space you are giving it: the text is centred in that space and layout.fits_height says whether it fit. Use type_data.fit to shrink the font into it.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Where the element lands in z-order, so you don't need a follow-up reorder_elements. `front` (default) puts it on top of what's already on the clip; `back` puts it behind everything — what a backdrop panel or a wash needs. `{before: element_id}` / `{after: element_id}` place it next to a specific element or group and put it in THAT node's group, which is the only way to position inside a group. Passing `group` as well changes what `front`/`back` address — see the Z-order notes in this tool's description. Placement is relative to the clip AS IT STANDS when the item is applied, so several `back` items in one call end up in REVERSE input order: send one backdrop per call, or list them back-to-front."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"string"},"required":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"],"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Same origin as the element's top-level x: `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Same origin as the element's top-level y: for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. y_anchor applies to the element's own y, NOT to keyframe values, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeColor":{"description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Animation keyframes (≥2 entries). Each entry has a timestamp plus one or more keyframable properties (position/size in canvas pixels; rotation°, opacity 0-100, scale, colors as #rrggbb[aa], etc. in native units). Easing: per-entry `interpolation` + optional per-property `interpolation_overrides`. Call get_element_schema to see which properties this element_type can keyframe. null is accepted and means none."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","maximum":9007199254740991,"minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"Label","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"start_time":{"description":"Appear at (seconds)","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific fields. Use the get_element_schema tool first to discover the shape for a given element_type. Omit when using component_id.","propertyNames":{"type":"string"},"type":"object"},"width":{"description":"Width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the WRAP width and it is kept as sent: the text wraps to it and the box stays this wide. (Height differs — the box's height follows the measured text.)","minimum":0,"type":"number"},"x":{"description":"X position in canvas pixels. `x` is the left edge, except TEXT where alignment picks it: left→left, center→centre, right→right (callout, rectangle, arrow, blur, spotlight, image and animation all use the left edge). For zoom, use type_data.centerX instead.","type":"number"},"y":{"description":"Y position in canvas pixels — for TEXT `y` is the vertical CENTRE when centre-aligned and the TOP otherwise; every other type uses the top. Pair it with y_anchor (below), always.","type":"number"},"y_anchor":{"description":"TEXT only. ALWAYS send `y_anchor` ('top'|'center'|'bottom') when you set `y` on text, so the position states what you meant instead of inheriting the alignment origin. To centre in a row, pill, chip, card or button: pass the container's centre line as `y` with y_anchor:'center'. The server centres the MEASURED box — you cannot compute that height, since wrapping and `fit` change it. A write-time DIRECTIVE: never stored, never returned by a read, so send it on every call whose `y` means the same thing. (Also accepted inside type_data — older spelling; this one wins.)","enum":["top","center","bottom"],"type":"string"}},"type":"object"},"minItems":1,"type":"array"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping; plus `placement` {y_anchor, top, center, bottom} — where the box landed and what your `y` meant, emitted only when the origin was implicit or resolves to 'center'. Compare placement.center with a container's centre line to verify centring: a mis-anchored label is the one layout error nothing else reports), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"}},"required":["project_id","elements"],"type":"object"}after
{"$schema":"http://json-schema.org/draft-07/schema#","additionalProperties":false,"properties":{"defaults":{"additionalProperties":false,"description":"Values shared by every item, sent once rather than repeated. Items win per key; type_data merges one level. E.g. defaults:{clip_index:1, element_type:'text', type_data:{font_setting:{font:'Inter',weight:'600'}, alignment:'center'}} then items carry only what differs.","properties":{"clip_id":{"type":"string"},"clip_index":{"minimum":0,"type":"integer"},"element_type":{"enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"minimum":0,"type":"number"},"group":{"minLength":1,"type":"string"},"height":{"minimum":0,"type":"number"},"insert_at":{"enum":["front","back"],"type":"string"},"keyframes":{"anyOf":[{"items":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items"},"type":"array"},{"type":"null"}]},"rotation":{"type":"number"},"start_time":{"minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"type":"object"},"width":{"minimum":0,"type":"number"},"x":{"type":"number"},"y":{"type":"number"}},"type":"object"},"project_id":{"description":"Project ID","type":"string"},"returning":{"description":"Declare what comes back, the way get_clip's `select` shapes a read. Omit it and you get ids, the measured layout, and the clip audit in compact form — which names every problem but drops the explanatory prose. Values: 'ids' (element_id per result), 'layout' (the measured text fit — font_size_px, text_width_px, natural_width_px, line_count, fits_width/fits_height/fits — `fits` is the WIDTH check (the widest rendered line inside the box), `fits_height` is the separate height one — plus an `adjusted` entry whenever the measured line box replaced the height you sent; widen a box to natural_width_px, NOT text_width_px, which is the widest RENDERED line and so a lower bound on text that is already wrapping), 'qa.codes' (audit counts and summary plus severity/code/element_ids per finding), 'qa' (the same audit with the full message and suggested fix on every finding — ask for this when a code alone isn't enough to act on). ERROR-severity findings always come back with their full message whichever you choose — including `returning:[]` and `['ids']`, which suppress WARNINGS only. A failed item always returns complete. Pass [] for just {total, succeeded, failed} plus any errors, the leanest write.","items":{"enum":["ids","layout","qa","qa.codes"],"type":"string"},"type":"array"},"updates":{"description":"Element updates — at least one entry.","items":{"additionalProperties":false,"properties":{"clip_index":{"description":"Zero-based clip index. OPTIONAL — `element_id` is unique within the project, so the server finds the clip itself. Pass it only as a fast-path hint; a wrong one is corrected rather than rejected.","minimum":0,"type":"integer"},"element_id":{"description":"Element ID (from get_clip)","type":"string"},"element_type":{"description":"Required only when providing type_data. Use get_element_schema in 'update' mode to see fields.","enum":["text","callout","rectangle","arrow","blur","spotlight","zoom","image","video","animation"],"type":"string"},"end_time":{"description":"New time the element disappears, in seconds from the start of the clip.","minimum":0,"type":"number"},"fit_to_lines":{"description":"Text elements only. Target line count for the element. After this entry's regular updates land, the server widens (then font-shrinks if widen can't fit) the element so the rendered text wraps to at most N lines without overlapping its neighbours. Uses the same compute path as director's fit_text_layout.","minimum":1,"type":"integer"},"group":{"description":"Move this element into a named group, creating it if this clip has none by that name. Pass an empty string to pull it back out to the clip root. Omit to leave grouping unchanged. Grouping never moves anything — coordinates stay absolute.","type":"string"},"height":{"description":"New height, pixels (0 allowed). For text, the space you are giving it — the box resizes to the measured text. See layout.fits_height in the response.","minimum":0,"type":"number"},"insert_at":{"anyOf":[{"enum":["front","back"],"type":"string"},{"additionalProperties":false,"properties":{"before":{"minLength":1,"type":"string"}},"required":["before"],"type":"object"},{"additionalProperties":false,"properties":{"after":{"minLength":1,"type":"string"}},"required":["after"],"type":"object"}],"description":"Move this element in z-order, without rebuilding it. `front`/`back` are the ends of the group it is already in (the clip itself for an ungrouped element), so they never change what it belongs to. `{before: element_id}` / `{after: element_id}` sit it next to that element or group and put it in THAT node's group — which is how you move something into or out of a group and position it in one call. Applied after `group` when both are passed."},"keyframes":{"anyOf":[{"items":{"additionalProperties":false,"properties":{"backgroundBlur":{"description":"Backdrop blur behind the element","type":"number"},"bezier":{"description":"Cubic-bezier [x1,y1,x2,y2]; used when interpolation is 'custom'.","items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"bezier_overrides":{"additionalProperties":{"items":{"type":"number"},"maxItems":4,"minItems":4,"type":"array"},"description":"Per-property cubic-bezier override, e.g. {\"opacity\":[.4,0,.6,1]}, when two `custom`-eased properties in this entry need DIFFERENT curves. Keys must be properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"]},"type":"object"},"blur":{"description":"Layer blur","type":"number"},"borderRadiusBottomLeft":{"description":"borderRadiusBottomLeft in canvas pixels","type":"number"},"borderRadiusBottomRight":{"description":"borderRadiusBottomRight in canvas pixels","type":"number"},"borderRadiusTopLeft":{"description":"borderRadiusTopLeft in canvas pixels","type":"number"},"borderRadiusTopRight":{"description":"borderRadiusTopRight in canvas pixels","type":"number"},"cropHeight":{"description":"cropHeight (native unit)","type":"number"},"cropWidth":{"description":"cropWidth (native unit)","type":"number"},"cropX":{"description":"cropX (native unit)","type":"number"},"cropY":{"description":"cropY (native unit)","type":"number"},"fillColor":{"description":"fillColor as #rrggbb or #rrggbbaa (alpha = opacity)","pattern":"^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","type":"string"},"fontSize":{"description":"Font size in CANVAS PIXELS (same unit as the top-level fontSize — NOT a fraction).","type":"number"},"height":{"description":"Height in canvas pixels (0 allowed)","minimum":0,"type":"number"},"imageBorderColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"imageBorderColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"imageBorderWidth":{"description":"imageBorderWidth in canvas pixels","type":"number"},"imageCornerRadiusBottomLeft":{"description":"imageCornerRadiusBottomLeft in canvas pixels","type":"number"},"imageCornerRadiusBottomRight":{"description":"imageCornerRadiusBottomRight in canvas pixels","type":"number"},"imageCornerRadiusTopLeft":{"description":"imageCornerRadiusTopLeft in canvas pixels","type":"number"},"imageCornerRadiusTopRight":{"description":"imageCornerRadiusTopRight in canvas pixels","type":"number"},"interpolation":{"description":"Easing for THIS entry, applied to every property in it (default easeInOut). NOT `easing`.","enum":["linear","easeInOut","easeIn","easeOut","sigmoid","smootherstep","smoothstep","hold","easeInBack","easeOutBack","easeInOutBack","custom"],"type":"string"},"interpolation_overrides":{"additionalProperties":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/interpolation"},"description":"Per-property easing override, e.g. {\"opacity\":\"linear\"} while the rest use `interpolation`. Keys must be keyframable properties present in this entry.","propertyNames":{"enum":["positionX","positionY","width","height","rotation","scaleX","scaleY","rotateX","rotateY","cropX","cropY","cropWidth","cropHeight","opacity","blur","backgroundBlur","strokeWidth","strokeDash","strokeGap","strokeTrimStart","strokeTrimEnd","strokeTrimOffset","textStrokeWidth","textStrokeDash","textStrokeGap","imageBorderWidth","shadowOffsetX","shadowOffsetY","shadowBlur","shadowSpread","borderRadiusTopLeft","borderRadiusTopRight","borderRadiusBottomRight","borderRadiusBottomLeft","imageCornerRadiusTopLeft","imageCornerRadiusTopRight","imageCornerRadiusBottomRight","imageCornerRadiusBottomLeft","videoCornerRadiusTopLeft","videoCornerRadiusTopRight","videoCornerRadiusBottomRight","videoCornerRadiusBottomLeft","fontSize","lineHeight","letterSpacing","paddingTop","paddingRight","paddingBottom","paddingLeft","textBgRadiusTopLeft","textBgRadiusTopRight","textBgRadiusBottomRight","textBgRadiusBottomLeft","fillColor","strokeColor","textStrokeColor","imageBorderColor","textColor","textBackgroundColor","shadowColor"]},"type":"object"},"letterSpacing":{"description":"Letter spacing in canvas pixels.","type":"number"},"lineHeight":{"description":"lineHeight (native unit)","type":"number"},"opacity":{"description":"Opacity 0-100","maximum":100,"minimum":0,"type":"number"},"paddingBottom":{"description":"paddingBottom in canvas pixels","type":"number"},"paddingLeft":{"description":"paddingLeft in canvas pixels","type":"number"},"paddingRight":{"description":"paddingRight in canvas pixels","type":"number"},"paddingTop":{"description":"paddingTop in canvas pixels","type":"number"},"positionX":{"description":"X position in canvas pixels (NOT `x`). Uses the SAME alignment-aware origin as the element's top-level x — left-aligned text uses top-left, center-aligned uses CENTER POINT, right-aligned uses top-right; all other types use top-left.","type":"number"},"positionY":{"description":"Y position in canvas pixels (NOT `y`). Origin matches positionX — center-aligned text positions by vertical center. y_anchor applies to the element's own y, not to keyframe values — keyframes use the alignment origin, so read the element back and key off its stored y.","type":"number"},"rotateX":{"description":"rotateX (native unit)","type":"number"},"rotateY":{"description":"rotateY (native unit)","type":"number"},"rotation":{"description":"Rotation in degrees","type":"number"},"scaleX":{"description":"Horizontal scale multiplier (1 = 100%)","type":"number"},"scaleY":{"description":"Vertical scale multiplier (1 = 100%)","type":"number"},"shadowBlur":{"description":"shadowBlur in canvas pixels","type":"number"},"shadowColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"shadowColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"shadowOffsetX":{"description":"shadowOffsetX in canvas pixels","type":"number"},"shadowOffsetY":{"description":"shadowOffsetY in canvas pixels","type":"number"},"shadowSpread":{"description":"shadowSpread in canvas pixels","type":"number"},"strokeColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"strokeColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"strokeDash":{"description":"strokeDash in canvas pixels","type":"number"},"strokeGap":{"description":"strokeGap in canvas pixels","type":"number"},"strokeTrimEnd":{"description":"strokeTrimEnd (native unit)","type":"number"},"strokeTrimOffset":{"description":"strokeTrimOffset (native unit)","type":"number"},"strokeTrimStart":{"description":"strokeTrimStart (native unit)","type":"number"},"strokeWidth":{"description":"strokeWidth in canvas pixels","type":"number"},"textBackgroundColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"textBackgroundColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textBgRadiusBottomLeft":{"description":"textBgRadiusBottomLeft in canvas pixels","type":"number"},"textBgRadiusBottomRight":{"description":"textBgRadiusBottomRight in canvas pixels","type":"number"},"textBgRadiusTopLeft":{"description":"textBgRadiusTopLeft in canvas pixels","type":"number"},"textBgRadiusTopRight":{"description":"textBgRadiusTopRight in canvas pixels","type":"number"},"textColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"textColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textStrokeColor":{"$ref":"#/properties/updates/items/properties/keyframes/anyOf/0/items/properties/fillColor","description":"textStrokeColor as #rrggbb or #rrggbbaa (alpha = opacity)"},"textStrokeDash":{"description":"textStrokeDash in canvas pixels","type":"number"},"textStrokeGap":{"description":"textStrokeGap in canvas pixels","type":"number"},"textStrokeWidth":{"description":"textStrokeWidth in canvas pixels","type":"number"},"timestamp":{"description":"Clip-relative time in seconds","minimum":0,"type":"number"},"videoCornerRadiusBottomLeft":{"description":"videoCornerRadiusBottomLeft in canvas pixels","type":"number"},"videoCornerRadiusBottomRight":{"description":"videoCornerRadiusBottomRight in canvas pixels","type":"number"},"videoCornerRadiusTopLeft":{"description":"videoCornerRadiusTopLeft in canvas pixels","type":"number"},"videoCornerRadiusTopRight":{"description":"videoCornerRadiusTopRight in canvas pixels","type":"number"},"width":{"description":"Width in canvas pixels (0 allowed — e.g. a bar that draws in from 0)","minimum":0,"type":"number"}},"required":["timestamp"],"type":"object"},"type":"array"},{"type":"null"}],"description":"Set keyframes for this element. Only the property tracks present in your entries are replaced — other tracks (color/rotation/typography/…) are preserved. Pass null OR an empty array to clear ALL keyframes. Same per-entry shape as add_elements; see get_element_schema for the keyframable properties of this element_type."},"keyframes_repeat":{"additionalProperties":false,"description":"Repeat `keyframes` as a cycle — { count, mode?, every? }. Requires `keyframes` in the same call.","properties":{"count":{"description":"TOTAL number of cycles, INCLUDING the one you wrote. 1 is a no-op. Expansion stops at the clip end rather than piling the overflow onto the last frame, and the generated total is capped at 600 keyframes (it errors rather than silently truncating).","minimum":1,"type":"integer"},"every":{"description":"Seconds between cycle STARTS. Defaults to the cycle's own span (back-to-back). A LARGER value holds the last value in the gap — that is how you get a pulse every 2s instead of a constant wobble. Shorter than the cycle is rejected, because the cycles would overlap and collide on the same frames.","exclusiveMinimum":0,"type":"number"},"mode":{"description":"'loop' (default) replays the cycle forwards every time — seamless only if your first and last values match. 'pingpong' alternates forwards/backwards, so the value always returns to where it started without a jump; use it for a breathe/pulse written as a one-way ramp.","enum":["loop","pingpong"],"type":"string"}},"required":["count"],"type":"object"},"name":{"description":"New label for the element.","type":"string"},"rotation":{"description":"Rotation in degrees","type":"number"},"save_as_component":{"description":"Animation elements only. Set true to save the current state as a reusable animation component (auto-named from the animation title), or pass a string to name it explicitly. Save runs AFTER any position/type_data updates land. Returns the component_id alongside the per-update result.","type":["boolean","string"]},"start_time":{"description":"New time the element appears, in seconds from the start of the clip.","minimum":0,"type":"number"},"type_data":{"additionalProperties":{},"description":"Type-specific updates. When provided, element_type is required. Get the schema via get_element_schema.","type":"object"},"width":{"description":"New width, pixels (0 allowed — e.g. a bar that draws in from 0). For text this is the wrap width and re-wraps the text.","minimum":0,"type":"number"},"x":{"description":"New X (canvas pixels). ORIGIN follows the element's alignment: text uses top-left / CENTER / top-right per alignment, all other types use top-left. Changing alignment in the same call re-anchors x/y to the new origin.","type":"number"},"y":{"description":"New Y (canvas pixels). Takes type_data.y_anchor the same way add_elements does.","type":"number"}},"required":["element_id"],"type":"object"},"minItems":1,"type":"array"}},"required":["project_id","updates"],"type":"object"}