Build scenes
Read a scene's context, write its HTML and land it through the real checks. 63 tools.
On the main list
The tools a client sees when it connects.
get_scene_context
Scene context
Everything to write ≤10 scenes against: brief, beats, words on the scene clock, bars, cues, look, neighbours. Free.
| Input | Type | Description |
|---|---|---|
scene_ids required | array | Scene ids (≤10). |
detail | "compact" | "full" | Default compact. |
get_authoring_contract
The vocabulary you author in
Optional kernels and document rules. No args = index; kernel:"<name>" = one in full (names:"a,b" = just those records); view:"shell" = skeletons (kernel:"<skeleton>" for one). Free.
| Input | Type | Description |
|---|---|---|
kernel | string | Kernel (shell: skeleton) |
view | "index" | "records" | "shell" | "full" | |
role | "knob" | "machinery" | "marker" | "receipt" | "published" | Narrow the records. |
names | string |
put_scenes
Land compositions
Land ≤20 HyperFrames compositions, one per scene, through the real gates; verdicts inline. <audio> is not mixed: voice goes in speech. SPENDS CREDITS for speech text. check:true lands AND looks in one call. Scenes alike: one template + per-scene data.
| Input | Type | Description |
|---|---|---|
scenes required | array | One per scene. |
dry_run | boolean | Validate only. |
check | boolean | string | Then look: true, or a preset (quick, layout-check, sync-check, player-check, dense-motion). |
async | boolean | |
template | string | One document for every scene that sends data: {{key}} is filled HTML-escaped, {{{key}}} raw. |
project_read
Read project files
Read ≤20 project files. Free.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
paths required | array |
project_write
Write a project file
Write a file (.json merges) through its owning service; a new scene folder creates the scene there. Diagnostics + diff. Guide: "Project as files".
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
path required | string | A file path. |
content required | string | object | |
dry_run | boolean | Verdict only. |
Callable by name
Not listed on connect, to keep the list short. Call them directly by name, or find them with tools.
add_element
Add an element to a scene
Add one element — motion design, an image, or a text box — to an existing scene. SPENDS CREDITS (the new element is generated). Use for "put a chart in scene 4" or "add a title card". Asynchronous; the scene shows as generating until it resolves.
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id. |
kind required | "motion-design" | "image" | "text" | What to add. |
prompt required | string | Creative prompt for motion-design/image, or the literal copy for text. |
at_seconds | number | Scene-local start time in seconds. Default 0. |
reference_scene_id | string | motion-design only: author the element to MATCH that resolved scene's composition design (layout, type, palette, motion). |
reference_image_url | string | motion-design only: a picture the author should LOOK AT (a library asset url, an import_asset result, a generate_image result). Keep prompt to intent — what to keep, what to change — rather than describing the picture. |
author_formula
Invent a new look — brand-mark motion or caption motion
Author a NEW look as a FORMULA: ingredients in a row, linted at write, instead of waiting for a code drop. One tool for both systems, chosen with slot.
slot:"bug" — the brand mark's motion. spec is {recipe, params} over seven carriers: three on the NOTATED clock, three on the MEASURED one, and one that BUILDS something.
Notated (a list of moments): "entry-exit" (in, out, rise, from, ease), "beat-envelope" (on, to, attack, decay, cooldown, prop) and "trigger-lift" (on, rest, attack, hold, release); on is a ruler — bar, beat, 2beat, hit, cam.
Measured (a value per frame off the analysed audio): "signal-follow" (channel, prop, to, rest, floor, gain, smooth) rides a channel continuously; "blob" (channel, size, color, wobble, swell) puts a backdrop behind the mark whose CONTOUR deforms; "ring" (channel, bars, radius, length, width, reach) surrounds it with a field of bars. The last two emit LAYERS behind the mark rather than moving the mark itself.
Built: "cta" (label, every | on, first, attack, hold, release, reach, pad, side, fill, color, size, weight, tracking, case, font) is the one recipe that does not just move the uploaded logo — the mark opens into a PILL, shows a line of type, and closes again. Use it for a subscribe prompt, a handle, a URL: anything the film should SAY periodically rather than wear permanently. The copy is a real text element in the render's own fonts, so it can be re-worded without touching the logo and it is sharp at any export size. Cadence is editorial — every seconds from first — because a call to action belongs every twenty seconds, not on every downbeat; name a ruler with on (usually "hit", from set_cues) when it should land on a moment instead. side is which way it opens, away from the frame edge the mark sits on. Reacts to no audio, so it works on a film with neither narration nor a bed.
channel is always qualified by its source — "vo:speech.loud", "music:band.low" — because a project with narration AND a bed has two different answers for "the audio". get_scene_context reports which sources a scene actually has, and draws the waveform.
You choose the slug. The row is HELD until you set status:"active".
slot:"caption" — how words move. spec is a caption formula: recipe, succession, accent, entry, life, exit, field, slot, paint, fonts. The id is MINTED from the title, because the renderer keys the compiled template on the row id; the reply tells you the id it used. Saved private — reaching the shared catalog is moderated, not a status flip.
Both are validated by COMPOSING the real thing and checking what comes out, not by matching a schema: either grammar can be valid JSON and still name an ingredient that does not exist, which renders as a duller look with no error anywhere. A bug spec that could strobe, reach outside its own element, or stop being the shape it claims to be is refused outright — amplitudes are capped, a blob's corners cannot leave the band where it is still a blob, and a layer cannot grow past the backdrop budget. Use list_formulas to see what exists. Free.
| Input | Type | Description |
|---|---|---|
slot | string | "bug" (brand mark motion, the default) or "caption" (how words move). |
slug | string | bug only: lowercase name for the look, e.g. "judder". Cannot shadow a built-in. |
title required | string | Display name, up to 80 characters. For a caption formula the id is minted from this. |
hint | string | bug only: one line the picker shows — what the viewer will SEE. |
spec required | object | The ingredients. bug: {recipe, params}. caption: {recipe|entry|life|exit|field|…}. |
tiers | array | bug only: mark tiers this suits — raster, svg, lockup, template. |
status | string | bug only: "held" (default), "active" to make it usable, "disabled" to retire it. |
batch_add_elements
Add an element to many scenes
Add the SAME element to MANY scenes in one call. Pick scenes with where (see get_inventory) or scene_ids; prompt is a template filled per scene with {chapter_title} {chapter_number} {scene_number} {scene_title} {index}. kind motion_design / image spend credits PER SCENE (text is free): call with dry_run:true first, show the targets and cost_estimate, confirm, then run. Elements resolve in place (poll get_project_status).
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
where | object | Scene selector: scene_ids[], chapter_ids[], family (title-card|stat|list|diagram|illustration|caption|media|engine|mixed, or an array), text_contains, title_contains, state (resolved|prompted|placeholder|generating|error), position (first-in-chapter|last-in-chapter), has_composition, cast. |
scene_ids | array | |
kind required | "motion_design" | "image" | "text" | |
prompt required | string | Prompt template. |
at_seconds | number | Scene-local start time (default 0). |
area | object | Grid placement {col,row,w?,h?}; omit for a centered 6×6. |
dry_run | boolean | Targets + quote only. |
max_targets | number | Cap on targets (default 20, max 60). |
reference_scene_id | string | motion_design only: author every added element to MATCH that scene's composition design. |
browse_themes
Browse themes as palettes
Show the available themes as what they actually are — palette, type and a description — instead of a list of slugs. Free. Use this whenever the user is CHOOSING a look, or when you are about to choose one for them: a theme picked by name alone is a guess at a visual decision, in a product where the visual decision is the point.
In a client that renders MCP Apps the user sees the palettes directly and can tell you which one they want. Elsewhere the same data comes back as text and you should describe two or three candidates and say why. Either way, name the reason for your pick so they can correct it.
PASS project_id WHENEVER THERE IS A PROJECT IN PLAY. With it the user can apply a theme by clicking it and you have nothing further to do; without it the picker can only tell them a slug to repeat back to you, which is a worse version of the same conversation.
To SUGGEST rather than list everything, pass slugs — your shortlist of 3-6, best first — and prompt, the question you are asking ("Three looks that suit a calm finance explainer — which one?"). The picker then shows only those.
| Input | Type | Description |
|---|---|---|
project_id | string | The project the user is choosing a look FOR. Turns the picker from a poster into a control: clicking a theme applies it. Omit only when no project exists yet. |
current_slug | string | The theme the project uses now, so it shows as already-chosen rather than as one of fifty equals. From get_project or get_theme. |
slugs | array | A shortlist to show instead of the whole catalog, best first. Omit to show every theme. |
prompt | string | The question the picker asks, shown above it — when you are asking the user to choose. |
build_footprints
A place's real plan from OpenStreetMap, as a 3D model
The mapped plan of a place (lat/lon centre, a square size_km across, up to 4 km) as ONE 3D model in your library, for window.__tcThree scenes: buildings extruded from their OpenStreetMap outlines (height from the map, else 5 m historic / 8 m modern), streets as paving, city walls as walls, with per-vertex colour; one mesh per part (walls, roofs, streets, city-walls) with UVs in metres, so each part can take its own tiling texture. roofs:"hip" gives every building a pitched four-sided roof instead of a flat top. historic_only:true keeps an archaeological site (features tagged historic, and features inside a mapped site of 5 ha or more) and drops the modern town around it. Pass origin_lat/origin_lon = a build_terrain frame's lat/lon and the plan shares its frame (metres; +x east, -z north, +y up; base at y = 0, so drape it by adding the ground height). frame also carries the city walls and every building centre, so procedural fill goes only where the map has nothing. Filed with its licence (ODbL: credit © OpenStreetMap contributors). The same request returns the same asset. Free.
| Input | Type | Description |
|---|---|---|
lat required | number | Centre latitude, degrees. |
lon required | number | Centre longitude, degrees. |
size_km | number | Width of the square, km (0.1–4). Default 1.5. |
origin_lat | number | The frame's origin latitude (a build_terrain frame's lat). Default the centre. |
origin_lon | number | The frame's origin longitude (a build_terrain frame's lon). Default the centre. |
historic_only | boolean | Keep only the historic site: what is tagged historic or lies inside a mapped archaeological site (default false). |
roofs | "flat" | "hip" | Roof form: 'flat' (default) or 'hip' (a pitched, four-sided tile roof on every building). |
name | string | Model name in your scenes and library, e.g. "pompeii-plan". |
build_terrain
Real terrain for a place, as a 3D model
The real ground around a place (lat/lon centre, a square size_km across) as ONE 3D model in your library, for window.__tcThree scenes: elevation from the open Terrain Tiles (SRTM, GMTED2010, ETOPO1 …), meshed on a grid with per-vertex colour (water, shore, lowland, hills, rock, snow), filed with its licence (cc-by: credit the elevation sources). A film shot in one place builds ONE terrain and every scene shoots it: a flyover, a dive to street level, a city you build on top. Returns asset_id, frame (metres; +x east, -z north, +y up; origin = lat/lon at sea level; m_per_deg_lat / m_per_deg_lon place any lat/lon on it; min_m / max_m) and usage: load it with { models: [{ name, src: "tc-asset:<asset_id>" }] } and the gltf@1 module. resolution is vertices per side (default 256, up to 512); exaggeration stretches height (default 1); sea_level:false keeps the sea floor. The same request returns the same asset. Free.
| Input | Type | Description |
|---|---|---|
lat required | number | Centre latitude, degrees. |
lon required | number | Centre longitude, degrees. |
size_km | number | Width of the square, km (0.5–200). Default 20. |
resolution | integer | Grid vertices per side (32–512). Default 256. |
exaggeration | number | Height multiplier (0.1–10). Default 1. |
sea_level | boolean | Draw ground below 0 m as flat water (default true). |
name | string | Model name in your scenes and library, e.g. "bay-of-naples". |
claim_scene
Say you are working on a scene
Take an ADVISORY, time-limited claim on a scene: "I am working on this" — shown as a lock on the scene in the editor, with your label. It never blocks anyone (other writers still land), and others holding the scene do not stop yours: they come back in held_by_others so you can back off or carry on. Call it again to renew (the heartbeat: about every third of the ttl while you work); it lapses on its own after ttl_seconds (default 90, max 900) or when you release_scene. Use it around a multi-step pass on a scene (read, author, check, land): claim at the START of the pass, so the person watching the editor sees you working through the thinking time between writes, and release_scene when you land it. Skip it for one quick write: a write already shows in the editor on its own. put_scene_composition / update_scene / edit_elements answer with claimed_by when ANOTHER actor holds the scene you just wrote to, so you can tell you may have overwritten them. Free.
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id (from get_storyboard). |
label | string | What the lock reads, e.g. "Claude · scene 4 pass". Defaults to the connection's name and session. |
ttl_seconds | integer | Seconds the claim lasts unless renewed. Default 90. |
copy_scenes
Copy scenes into another project
Copy scenes (scene_ids, or every scene of a chapter_id) from from_project into to_project BY REFERENCE: the composition is shared by its immutable url, so its pinned modules and linked kernels come along and nothing is pasted; the call and its answer are a few hundred bytes. Carries the source, scene metadata, entry/exit state, length, title and the scene's own sound (include_sound:false drops it). Narration and voice travel with a scene when every scene of its script block is copied (the block and its voice file land in the target chapter); a scene whose block is only partly selected is a silent scene of the same length (narration:false forces that). move:true then soft-deletes the source scenes and archives the narration blocks they emptied, so it moves rather than copies; organize op restore {archive_id} undoes it. A whole chapter_id copied into a new chapter also brings the chapter's sound: its score cue and music/sfx clips, the conductor (bar 1 moved with the chapter) when the target has no tempo map, and the music/ program files when the target has none, so it plays as it did with no re-render (result.sound says what came). Lands in to_chapter_id, or in a new chapter named after the source one; position is 1-based. A scene made for another shape than the target (16:9 into 9:16) takes the target's frame and fits whole inside it when it is all full-frame gen:html; otherwise it keeps its own (scenes[].frame says which). Needs read on the source and write on the target. Returns the new scene ids. dry_run:true plans it. Free.
| Input | Type | Description |
|---|---|---|
from_project required | string | Source project (read only). |
scene_ids | array | Scenes to copy, in this order. |
chapter_id | string | Or: every scene of this source chapter. |
to_project required | string | Target project (written). |
to_chapter_id | string | Target chapter. Omit for a new one. |
position | number | 1-based position among the target chapter's scenes. Omit to append. |
include_sound | boolean | Carry each scene's own music and sfx, and for a whole chapter into a new one its score cue, clips, conductor and music/ files. Default true. |
narration | boolean | Carry the narration and voice of whole script blocks. Default true. |
move | boolean | After copying, soft-delete the source scenes (restorable). Default false. |
dry_run | boolean | Plan only; nothing written. |
create_scene
Create a scene in a chapter
Create ONE scene in an existing chapter. A scene need not have narration: give it a duration and describe it (brief); add narration only when words should be spoken. Free — a prompt makes a DRAFT composition scene (no visuals until someone writes it: put_scene_composition with your own HTML, or resolve_project to have Typecut compose it), a media_url makes a resolved media scene. In an anchored chapter pass block_id + anchor_text — an EXACT quote from that block's narration where the scene starts; the first scene of a block anchors at its opening words. Adding several scenes to the same block: one call at a time, never in parallel. Pass reference_image_url when the scene should be made FROM or LIKE a picture. Ids come from get_storyboard. For bulk structure (many scenes, chapters) use apply_changes.
| Input | Type | Description |
|---|---|---|
chapter_id required | string | Chapter id (from get_storyboard). |
block_id | string | Narration block to attach the scene to. |
anchor_text | string | EXACT substring of the block's text where the scene starts. Requires block_id. |
narration | string | SPOKEN words only. Put the scene at the END of the chapter, in a NEW narration block with this text — what "add a scene at the end of chapter 2" means. The block is voiced on the next resolve and its voiceover sets the scene's duration. Do NOT pass block_id/anchor_text/brief with it. A scene need not have narration — for one with nothing spoken use brief. |
brief | string | For a scene with NO spoken words ("no narration", "just the music"): what it shows. Written at the END of the chapter as a non-spoken script line — never voiced — and the scene keeps duration (default 8s). Also its prompt when none is given. Do NOT pass narration/block_id/anchor_text with it. |
title | string | Short scene title. |
prompt | string | Creative brief for a draft composition scene — intent, not a picture description. |
duration | number | Seconds — the scene's own length when nothing spoken sets it (with brief: default 8, otherwise 5). |
media_url | string | Public image/video url for a resolved media scene (use a library url). |
is_video | boolean | Whether media_url is a video. |
position | number | 1-based insert position in the chapter. Omit to append. |
reference_image_url | string | A picture the author should LOOK AT (library asset url from list_media / import_asset / generate_image). Draft scenes only. |
edit_composition_unit
Edit a unit inside a composition
Edit ONE unit inside a gen:html motion-design composition. Deterministic ops are instant and free: delete_unit, hide_unit, show_unit, set_text (pure-text units), move_unit (left/top design px), resize_unit (width/height px), set_style (whitelisted CSS: color, background, background-color, opacity, fill, stroke, stroke-width, border-color, border-radius, font-size, font-weight, letter-spacing, font-family — single quotes in font stacks, never double), duplicate_unit, reorder_unit (direction front|forward|backward|back), retime_tween / delete_tween (pass at = the segment's current start from inspect_composition's lanes; retime also takes start). llm_edit is the free-form fallback — an instruction executed by the edit model (~30-60s, SPENDS CREDITS, asynchronous); prefer a deterministic op whenever one fits. Unit keys come from inspect_composition; "sel:"/"@" keys are not editable.
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id. |
element_id required | string | The composition element id (from inspect_composition). |
op required | "delete_unit" | "hide_unit" | "show_unit" | "set_text" | "move_unit" | "resize_unit" | "set_style" | "duplicate_unit" | "reorder_unit" | "retime_tween" | "delete_tween" | "llm_edit" | The edit to apply. |
unit_key | string | Unit key from inspect_composition. Required for every op except llm_edit. |
text | string | set_text: replacement text (1-400 chars). |
left | number | move_unit: new left (design px). |
top | number | move_unit: new top (design px). |
width | number | resize_unit: new width (design px). |
height | number | resize_unit: new height (design px). |
styles | object | set_style: 1-8 whitelisted CSS property -> value entries. |
direction | "front" | "forward" | "backward" | "back" | reorder_unit: z-order move among visual siblings. |
at | number | retime_tween/delete_tween: the segment's CURRENT start (seconds). |
start | number | retime_tween: the new start (seconds). |
instruction | string | llm_edit: what to change (1-4000 chars). |
idempotency_key | string | Optional retry-dedup key: pass the same key when retrying an identical call so it is not applied twice (matters for duplicate_unit/reorder_unit). |
edit_elements
Edit elements in a scene
Move, resize, retime, recolour or delete elements already in a composition, by patching the scene source line-by-line. Free — no generation. Call get_scene first: patches address source by line number, and passing the scene's composition_version as expected_version turns the write into a compare-and-set, so a concurrent editor save returns a 409 instead of one of you silently losing work.
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id. |
patches required | array | ElementEditPatch objects — each targets a sourceLine and carries edits (find/replace, setToken, shiftPosition, setTimingPrefix, …). |
expected_version | number | The scene's composition_version from get_scene. |
expect_lines | array | Staleness gate: [{line, text}] pairs from your last read of the source. If any line no longer matches, the write returns a conflict instead of silently applying patches to moved lines. Strongly recommended alongside expected_version. |
export_hyperframes
Export the project as a HyperFrames folder
The whole project as a plain HyperFrames folder, zipped and hosted (the url is in the result): index.html is the film, each scene a sub-composition at its window on the film clock (data-chapter names its chapter), with the captions layer and the voice, music and SFX as <audio> tracks; compositions/ holds one document per scene (a scene you authored is your own document back; a structured scene is compiled output, read-only); typecut.json lists node paths, windows and versions. Upstream npx hyperframes render . plays it as is. Edit the folder locally, then send it back with import_hyperframes. Free.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
export_scene
Export one scene
Export ONE scene as a file, the same way the app does. Formats: mp4, png, gif, png_sequence, webp_animated, mov_prores_alpha (coming), web_block. png answers at once with full-size frames from the MP4 renderer (t, or times up to 24; counts toward the look allowance). mp4 starts a render (SPENDS CREDITS; a current cached clip comes back at once; force: true renders again) and returns generation_id for get_status. web_block takes options.manifest (see export_web_block). A format marked coming is refused with not_available until the server makes it. Finished pieces or on request only: check work with preview_frames.
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id. |
format required | "mp4" | "webm_alpha" | "png" | "gif" | "png_sequence" | "webp_animated" | "mov_prores_alpha" | "audio" | "srt" | "vtt" | "premiere" | "resolve" | "finalcut" | "nle_package" | "hyperframes" | "web_block" | Format id (export_formats). |
options | object | Format options (export_formats lists each format's), e.g. {t: 2.5} for png, {resolution: "720p"} for mp4. |
find_exemplars
Find worked shots for a beat
The k worked shots of a skill nearest to the beat you are writing, as references by default: id, title, brief, intents, duration and code_chars. Pass code:true for the shot code too (it can be large: check code_chars first). Adapt them; do not copy their places. Free.
| Input | Type | Description |
|---|---|---|
skill | string | The skill slug, e.g. "history-map". Omit it (or "*") to search every active skill and merge by score. |
brief required | string | One line: what happens in the beat (travel, a deal, a recolour, a crowd, a reveal…). |
k | number | How many (default 3, max 12). |
intents | array | Optional tags that should match: travel, deal, recolour, crowd, river, reveal. |
code | boolean | Inline each shot's code (default false: references only, with code_chars so you can size the read before asking). |
generate_scene
Generate a single scene
Author ONE motion-designed scene from intent — no project needed. SPENDS CREDITS. Use when the user wants a single piece rather than a whole video: a title card, an explainer beat, a visual for something they are writing.
You supply the INTENT and the materials; Typecut decides how it looks and moves. Describe what the scene should convey rather than how to build it. If you would rather write the HTML yourself, skip this tool: create_project authoring:"own", then get_authoring_context → put_scene_composition. To make it SPEAK, pass speech clips (a line in a voice, a speak asset, or a recording) — they are resolved once, placed by their anchors, mixed with a music bed, and the motion, captions and word: cues follow the heard words. narration alone is only text for the author, not a voice.
Asynchronous — returns a scene id AND a share_url immediately. Hand the user the share_url straight away: the page is live before the scene finishes and fills in as it generates, so they can watch rather than wait on you to poll.
| Input | Type | Description |
|---|---|---|
workspace_id required | string | Workspace the scene belongs to (from list_workspaces). |
prompt required | string | What the scene should show and do — the creative brief for this one scene. |
narration | string | What the scene is about in words, for the author. NOT voiced — use speech for a voice. |
speech | array | Speech placed by the scene, one clip per line. source: {text, voice_id|clone_id} (spoken on landing, SPENDS CREDITS, cached), {asset_id} (a speak result) or {media_id, from?, to?} (a slice of a transcribed recording). at: seconds | "bar:3" | "beat:5" | "after:<id>+400"; omit to follow the previous clip. The scene lasts as long as its speech (+0.6s) unless it has a planned duration (update_scene {duration}, or a plan clock other than speech:true), and overrunning that is refused. cuts / fx: typecut://studio/guide "Voice inside the scene". |
music | object | |
score | object | |
theme_slug | string | Theme from list_catalog("themes"). Defaults to the workspace look. |
duration_seconds | number | Scene length in seconds. Default: the speech length (+0.6s) when the scene speaks, else 10. |
aspect_ratio | string | Frame shape: "16:9", "9:16", "4:3" or "1:1" — anything else is rejected. Defaults to the scene's current shape when regenerating, otherwise the workspace default. Pass "9:16" for vertical/social. |
media_asset_ids | array | Library asset ids to build the scene around. |
scene_id | string | Re-author an EXISTING scene instead of creating one — a standalone scene, or a project's motion-design scene. Pair it with media_asset_ids to make footage or a photo the ground the composition is built over: attach_media would REPLACE the composition, this authors it AROUND the asset. The scene keeps its duration and frame. |
reference_image_url | string | A picture the author should LOOK AT — make the scene FROM or LIKE it. A library asset url (list_media / import_asset / generate_image). Keep prompt to intent. |
get_authoring_context
What is known about this beat
Everything the pipeline knows about a beat, BEFORE you author it. Read this first whenever you are going to write or rewrite a composition — without it you are guessing at numbers the system already has.
Per composition element it returns: the BRIEF it was planned with; the numbered BEATS resolved to real seconds (the director quotes a narration phrase, the pipeline joins it to the word timeline — a beat that never matched says so); the canvas and this element's own window and start on the scene clock; this scene's VO WORD TIMELINE, scene-relative, so a reveal can land on the word that says it (for a scene with its own speech clips, their words — fx marks a word heard through an audio effect; top-level audio lists the clips, their FX and the music bed); the MUSICAL GRID re-based to the element's own zero; the theme's resolved COLOUR and TYPE ROLES, its image style and the actual ground; the CAMERA ITINERARY that will move over the composition; measured ANCHORS; the previous and next scene, so the picture neither repeats nor pre-empts them; and the CAST, generated IMAGERY and user LIBRARY it may reuse verbatim instead of inventing something new.
Timing, theme resolution, text-fit geometry and camera authority are the pipeline's, not yours: read them here and author AGAINST them rather than hard-coding equivalents, which is the single most common way a composition comes out wrong. Free, read-only. Pair it with inspect_composition, which describes what is already IN a composition; this describes what the composition is FOR.
Each element carries packet: the same facts resolved and layered for an author — the contract rules for its KIND, the job (intent, canvas, words, beats, assets, and the exact theme CSS var names injected at mount), neighbours, signals, and gaps naming what the pipeline does not know yet. The packet IS the answer: by default the reply is lean and states each fact once (the brief is packet.job.intent, the words packet.job.words, the beats packet.job.beats). view:"full" adds the raw envelope our own composer is handed, for debugging only.
A project made with a SKILL (get_project_skill) returns top-level look instead of theme variables: the skill ref and its resolved tokens (the skill defaults overlaid with the project's palette/type). Paste those tokens on your own explicit-size .stage. The look is THIS scene's: a style set for its scene or chapter beats the project's. Top-level skills lists what else is in force: the style ref, the recipe(s) (how the film is made: load with get_skill) and the cast (characters shared across scenes). Refs only, never skill bodies: get_skill loads one on demand.
Authoring several scenes? Each reply carries known; pass it back on the next scene and the project-wide sections you already hold come back as {same: hash}.
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id (from get_storyboard or get_inventory). |
element_id | string | Narrow to ONE composition element. Omit for every gen:html in the scene. |
detail | string | Packet detail: "compact" (default) or "full" (adds the project gist, every word, all signals and references). |
kind | string | Force a composition kind: explainer-beat, data-chart, music-visualizer, sticker, title-card or media-frame. Omit to use the inferred one. |
view | string | "lean" (default): each fact once, in the packet. "full": also the raw envelope and the brief repeated — several KB more per scene; only for debugging. |
known | string | The known token from your previous get_authoring_context reply in this project. The sections you already have (assets, chapter voiceover, contract, theme, gaps, look) come back as {same: hash}, about half of each reply. Omit on the first scene. |
get_composition_source
Read the composition a scene mounts
The HTML text of the composition a scene mounts — the hosted document put_scene_composition (or a generation) landed — with its url and size in bytes. Read it back to see exactly what you landed, edit it, and land it again. A scene with several compositions: pass element_id (the result lists them). Free.
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id. |
element_id | string | Which composition, when the scene mounts several. Omit for the first. |
authored | boolean | Your last-sent source (what edits patch). |
get_module
Read a module
One hosted module: its url, parameter schema (params, state), exports, requirements, every hosted version, and the pin tag to reference it from a scene. id is "cast:pip" (latest) or "cast:pip@2". Its API MANIFEST (api_text): every function with its parameters and one line on what it does, and the name tables its string arguments draw from (name ∈ CUTS — the values that exist) — read that to call a kit, never its body. Purposes come from the module header's api block, else the author's comments, else a model (marked [inferred]). source:true also returns the file. Free.
| Input | Type | Description |
|---|---|---|
id required | string | "cast:pip" for the latest, or "cast:pip@2". |
version | number | A specific version. |
source | boolean | Also return the file itself. |
structured | boolean | Return the same content as a JSON object (text and structuredContent) instead of the prose alone (default false). The /v1 route always carries both. |
get_project_skill
Read the project's skills
The skills the project is made with. skill is the project-wide STYLE (ref slug@version, the palette and type overrides, the legacy theme roles derived from its tokens), or null; skills lists every entry: {ref, role: style|recipe|cast, scope: 'project'|{chapter_id}|{scene_id}, palette?, type?, note?}. A project can hold several: styles that differ by chapter or scene (a synthwave chapter, a blueprint chapter), a RECIPE for the whole film (explainer-film, long-film-structure…) that sits on top of a style, and CAST (character skills shared across scenes). With scene_id you also get effective: what is in force for that scene (style = scene > chapter > project, the recipes, the cast) with the style's resolved tokens: refs and tokens only, never skill bodies (get_skill loads one). get_authoring_context returns the same for the scene it describes. Free.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
scene_id | string | Also resolve what is in force for this scene. |
get_scene
Get one scene
Read a single scene in full: prompt, background media, timing, caption overrides, and the composition source of a scene Typecut designed. Before edit_elements, read it — and keep the returned composition_version, which edit_elements takes as expected_version so a save from someone with the editor open cannot be silently clobbered.
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id (from get_storyboard). |
get_scene_brief
One scene, studied — frames, what arrives when, colours, its place in the arc
Study ONE scene in about a thousand tokens: real frames (stored for this scene version, else one quick capture, else older ones labelled older), WHAT ARRIVES WHEN against the spoken words (each on-screen element's arrival time, whether it stays to the cut, and the narration word it names and how far its arrival is from that word), the colours measured from the pixels, the authoring brief, the narration, its neighbours, what the project's cached gist made of it (job, device, role), and for a hand-written scene its OWN CODE (the author's script, not the stored document: no runtimes, no module body, no preamble the gist already named). The drill-down from get_project_gist: the gist names each technique's best example scene; read that scene here before building something like it, instead of reading 150KB of markup or guessing. Unlike the gist it may name the subject — you are studying this scene on purpose. Works for a scene of any project you can read. capture:false reads only stored frames. Free; stores any frames it captures for the next reader.
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id (from get_storyboard, or a gist's device example). |
capture | boolean | Capture frames when none are stored (default true). false = read what is stored only. |
structured | boolean | Return the same content as a JSON object (text and structuredContent) instead of the prose alone (default false). The /v1 route always carries both. |
get_theme
Get the project theme
Read the project's visual identity — theme slug, colour roles, font roles, light/dark. Read before restyling so you patch roles that exist.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
get_world_manifest
Map of a world
What a world module (env:rome...) contains, in one small answer (~5 KB, not its 30 KB source): regions (what each builds, tri and build budgets), anchors (name, bounds, regions that build it), seeds, units. Read it before framing shots so you pick the region that builds what you frame. With region + camera {pos, target, fov, aspect?} it also checks that region builds everything the camera sees: framing.omitted lists anchors that would be missing from the frame, suggest_region the cheapest region that builds them all. module is "env:rome" (latest) or "env:rome@5". Free.
| Input | Type | Description |
|---|---|---|
module required | string | "env:rome" for the latest, or "env:rome@5". |
region | string | With camera: the region the shot builds. |
camera | object | The shot's camera in world units: pos [x,y,z], target [x,y,z], fov (vertical degrees), aspect (default 16/9). |
harvest_skill
Draft worked shots from a finished project
Staff only. Draft exemplars for a skill from a finished project: per scene the shot code, the heard words, the camera keys and three preview frames, as draft rows to curate with put_skill_exemplars. Pages ≤12 scenes a call (offset, next_offset). frames:false skips the previews. Free.
| Input | Type | Description |
|---|---|---|
skill required | string | The skill slug the shots belong to. |
project_id required | string | The finished project. |
scene_ids | array | Only these scenes. |
offset | number | Start at this scene (from next_offset). |
limit | number | Scenes this call (default 6, max 12). |
frames | boolean | Render preview frames (default true). |
overwrite | boolean | Redraft scenes already curated. |
import_html
Import a single-file HTML video and split it into scenes
Bring a video you (or Claude) already made as ONE HTML file — a master GSAP timeline, every scene inline, often an <audio> tag — into Typecut as a chapter of scenes on the timeline, each editable, re-renderable and exportable on its own. New project: workspace_id (+ name). Existing one: project_id (a chapter is appended).
How it splits. Inline HyperFrames sub-compositions (a <template id="X-template"> + host, or a self-contained inline composition) become scenes with their own documents on their own clock. Anything else is cut by TIME, at the first of: GSAP labels (tl.addLabel), the visibility windows of the root's top-level sections, full-frame clears, and — when the file has none of those — shot changes found on a low-res render; cuts: [seconds] overrides all of it. The file is hosted ONCE and each scene is a small wrapper that plays its window of that one copy, so every scene is pixel-identical to the original by construction. To really edit a scene, replace its document with put_scene_composition (local t=0); its neighbours do not move.
Audio. The root <audio> becomes the project music (audio: "music", default, when the chapter opens the film), or the VOICE (audio: "voice": each scene gets its part as a speech clip on the transcript — words, captions, cues — and cuts move off spoken words; the recording needs a transcript first: the result names the media_id to run transcribe_media on, then call again with chapter_id), or is left out ("none"). Relative src/href in the file need base_url.
Non-seekable code (requestAnimationFrame loops, timers, Date.now) is flagged: the renderer seeks, it never plays. verify runs the render-twice check on every scene; with video_url (your own render of the file) a scene that fails it becomes a clip of that render. dry_run returns the analysis and the proposed cuts and creates nothing — do it first. Large files: create_upload, curl, then src_url. Free (transcribe_media is priced on its own).
| Input | Type | Description |
|---|---|---|
html | string | The HTML file. Or src_url. |
src_url | string | A create_upload public_url you PUT the file to (for files over ~100 KB). |
base_url | string | Where the file lived (e.g. its folder URL), so its relative images, fonts and audio resolve. |
workspace_id | string | Create a new project here. |
name | string | New project name. Defaults to the file's <title>. |
project_id | string | Append a chapter to this project instead. |
chapter_id | string | Finish an earlier import into this chapter (same file, same options): re-lands its scenes. |
chapter_title | string | Defaults to the file's <title>. |
cuts | array | Cut at these seconds of the file instead of detecting. |
duration_sec | number | The film length, when the file does not declare it. |
min_scene_sec | number | Drop cuts closer than this. Default 1. |
max_scene_sec | number | Split longer stretches evenly. Default 20. |
audio | "music" | "voice" | "none" | What the root <audio> becomes. Default music. |
shot_detection | "auto" | "off" | "force" | auto (default): only when the file offers no structure. |
subcomps | boolean | Split inline sub-compositions into their own documents. Default true. |
video_url | string | Your own render of the file (mp4): a scene that fails the render check becomes a clip of it. |
verify | boolean | Render-check every scene (seek-order). Default: when the file has non-seekable code. |
captions | boolean | Burn in captions on a new project. Default false (the file has its own type). |
dry_run | boolean | Analyse and propose the cuts; create nothing. |
import_hyperframes
Import a HyperFrames folder
Send an edited HyperFrames folder back: the files you changed (compositions/*.html, each keeping its <meta name="typecut:scene"> line) or the whole folder with index.html. Only documents that differ from the project's current ones land (through put_scene_composition, same gates), so only those scenes re-render. A top-level sub-composition in index.html with no scene behind it becomes a new scene, in the chapter its data-chapter names (else the last). A document exported from an older version of its scene is refused as a conflict unless force. Voice, music and captions stay Typecut's; root <audio> is not imported. dry_run validates every changed document and lands nothing. Scene documents are large: upload each changed one with create_upload and pass {path, src_url} rather than pasting it. Free; asset slots in a composition may spend.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
files required | array | The folder files: changed scene documents, optionally index.html. ≤200 files, ≤12 MB. |
dry_run | boolean | Validate every changed document; land nothing. |
force | boolean | Land even when a document was exported from an older version of its scene. |
list_formulas
List every look you can pick or author
The whole formula catalog across both systems: brand-mark motion (slot "bug" — the built-ins marked built_in, every active authored row, and your own held drafts) and caption motion (slot "caption" — the published catalog plus your own private ones). Pass slot to narrow it. Use before set_branding or set_captions to see which values are valid, and after author_formula to check a draft landed. Free, reads only.
| Input | Type | Description |
|---|---|---|
slot | string | Narrow to "bug" or "caption". Omit for both. |
list_modules
List modules
The modules hosted on this account — cast, devices, UI, kits — each at its latest version. Default is COMPACT and 25 per page (id, kind, version, bytes, description; the whole list is ~14k tokens): narrow with kind (id prefix: env, model, kit, rig, cast, map...) or q (words: "mocap", "walk", "pick up" match ids, descriptions and api lines), page with cursor (the answer carries total and next_cursor), set limit up to 500. view:"full" adds schema, url and pin tag; id returns every version of one module in full. Free.
| Input | Type | Description |
|---|---|---|
id | string | Only this module, every version (full view). |
kind | string | Only this kind: the id prefix before ":" (env, model, kit, rig, cast, map...). |
q | string | Words to find (case-insensitive): an id substring, or every word in the description or api. Id matches first. |
limit | number | Page size (default 25, max 500). |
cursor | string | The previous answer's next_cursor. |
view | "full" | "compact" | compact (default unless id): id, kind, version, bytes, description. full: schema, url, pin tag. |
list_recipes
List or read saved recipes
Free. Your saved recipes: the model choices you (or an earlier session) settled on, as steps {tool, model, params, fallbacks, note}. With no name, lists them; with a name, returns that recipe in full. Re-apply one by passing each step's model and params to its tool; if a step's model fails, try its fallbacks in order. Models are ids from list_catalog("models").
| Input | Type | Description |
|---|---|---|
name | string | A recipe name. Omit to list every recipe. |
merge_scenes
Merge two adjacent scenes
Join two ADJACENT scenes of one chapter into ONE scene that plays the first, then the second: both compositions on one clock (the second's lines move by the first's length; a clashing element id of the second gets a _b suffix, listed in renamed), lengths summed, and every voice kept. In one narration block the scene covers both slices; across two adjacent voiced blocks both blocks and both voices stay and the scene carries on over the second one. Free, nothing re-voiced. A pair it cannot combine exactly (differing canvas or world, a time form it cannot move, a voiced block next to an unvoiced one) is refused with the reason and nothing changes. To merge a run of scenes, plan the whole run as merge_scenes ops in ONE apply_changes with dry_run: true (it checks each merge against the scenes and lists what each would do), then apply the same ops. Runs as one changeset: undo_changes with the returned changeset_id reverts it. dry_run:true projects it without writing. Free.
| Input | Type | Description |
|---|---|---|
scene_ids required | array | The two scenes, in either order. |
dry_run | boolean | Check it and say what it would do; nothing written. |
move_scene
Move a scene to another chapter
Move a scene to a position in another chapter of the same project (the same chapter just reorders it). The scene travels with its composition and length; its narration stays behind (the words belong to that chapter's voice, and its neighbours cover them). In a narrated target chapter it is placed with a non-spoken line holding its title, like a create_scene brief. Runs as one changeset: undo_changes with the returned changeset_id reverts it. dry_run:true projects it without writing. Free.
| Input | Type | Description |
|---|---|---|
scene_id required | string | The scene to move. |
chapter_id required | string | Target chapter (get_storyboard). |
position | number | 1-based position among the target chapter's scenes. Omit to append. |
dry_run | boolean | Project only; nothing written. |
project_ls
List the project as files
Any directory segment may be the id instead, or the number alone. Each entry carries its id, size and state (scene lifecycle, tts ready/stale/missing, a composition hosted or empty). path narrows to one chapter or scene directory. Read with project_read, change with project_write / project_mv / project_rm. Free.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
path | string | Only this directory (a chapter or scene). Default: the whole project. |
after | string | next_cursor from the previous page; omit for the first page. A page is about 40 KB. |
project_mv
Move a scene or chapter
Move a scene directory to a position (chapters/<chapter>/<NNN>, or chapters/<chapter>/ to append) — within its chapter, or into another one (re-created there with its script block and composition copied; it gets a NEW id, returned in created). Move a chapter directory to chapters/<NN>. One apply_changes batch: recorded and undoable with undo_changes. dry_run returns the plan. Free.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
from required | string | A path in the project tree, e.g. "chapters/02-setup/003-the-map/scene.json". An id works for any directory segment. |
to required | string | The target path. |
dry_run | boolean | Validate and return the diff and verdict; write nothing. Free. |
project_rm
Delete a scene or chapter
Delete a scene directory (with its script block when no other scene shares it) or a chapter directory (with its scenes). Recorded and undoable with undo_changes. Files are views of those entities: write them instead. dry_run returns the plan. Free.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
path required | string | A path in the project tree, e.g. "chapters/02-setup/003-the-map/scene.json". An id works for any directory segment. |
dry_run | boolean | Validate and return the diff and verdict; write nothing. Free. |
put_module
Host a module once, pin it by version
Host a piece of HyperFrames code that many scenes share — a character rig (cast:pip), a recurring device or UI element (device:world, device:prompt-box), helpers (kit) — ONCE, at an immutable url per version, instead of pasting it into every scene. Copies drift; a pinned reference does not.
The file declares itself in a header: /*@module {"id":"cast:pip","version":2,"exports":"CastPip","requires":["kit@1"],"params":{"face":{"type":"enum","values":["curious","worried"]},"lean":{"type":"number","unit":"deg"}},"state":{…}} */. params is the vocabulary a shot may pass (types: number integer string boolean enum object array function any). Every problem comes back at once.
Readers learn a module from its API manifest (api_text, also on get_module), derived from the file: each exported function's signature, the comment line above it, and the name tables its arguments draw from. Write a one-line comment above each function, or name them in the header's "api":{"wave":"Raise one arm and wave twice"} — that wins; uncommented ones get a model-written line, marked [inferred].
A scene pins it with the returned pin tag: <script data-tc-module="cast:pip@2" src="…"></script>, requirements first. A version never changes; putting a new version (a bump) returns rebase: your scenes still pinned to an older one. Upload the file with create_upload and pass src_url so it never enters this conversation. dry_run:true checks the header and reports the rebase list, hosting nothing. Same access as landing authored compositions. Free.
| Input | Type | Description |
|---|---|---|
id required | string | The module id without its version, e.g. "cast:pip", "device:world", "kit". |
version | number | The version to host. Default: the header's, else the latest + 1. |
source | string | The module file. Exactly one of source / src_url. |
src_url | string | A create_upload public_url holding the file. |
project_id | string | Only look for older pins in this project. |
dry_run | boolean | Validate and report, host nothing. |
put_report
Answer as an animated scene from a compact spec
Turn a short report into an animated 16:9 scene WITHOUT writing HTML: send a JSON spec of beats and the server sets the scene length, mounts the kit:report runtime and lands it through the same gates as put_scene_composition. About 300 tokens of JSON for a 6-beat report; use it whenever speed and density matter more than a custom look.
spec = {look? "doodle" (hand-drawn marker-on-paper), title?, sub?, accent? "#hex", light? true, theme? false (a themed project is followed by default; false keeps the built-in palette), speed? 0.5-3, beats: [BEAT, …]} (4-6 beats is a good answer; the first beat is the verdict). BEAT = {k, h (heading), d? seconds (default from content)} plus: every beat may also carry say "a spoken line" (write it FIRST in the beat: it is voiced in the browser on the live page and sets the beat length when it is the longer; keep it under ~100 characters) and sfx tick|pop|whoosh|chime|pen|thud|ding|none (each kind already has a sound; rarely needed). Top level: voice "fast" (server Kokoro voice for every say, all lines spoken at once when you write; ~1 s extra, small credit cost; "fast:Michael" picks another library voice), "studio:Adrian" (any library voice by name, e.g. a Fish voice; costs that voice rate), "kokoro:af_heart" (in the browser) or "device", music {mood?, src? https audio url, volume? 0-1} (a soft pad for the mood when there is no src). Sound plays only on /share/{token}/live after the viewer taps for sound. svg svg "<path/>…" (freeform drawing on a 1600x640 canvas: path line circle ellipse rect polyline polygon text only, drawn on stroke by stroke), h? img src https url (e.g. a preview_composition frame url) or handle "tc:scene/<id>" / "tc:asset/<id>", cap? (a taped picture) track handle "tc:audio/<analysis id>" (from analyze_audio / get_audio_analysis), from? seconds, d? seconds (default 12): section bands by energy, downbeat ticks, kick and snare lanes, salient pins, tempo and key, a playhead; the live page plays the track itself audio items [[label, https audio url, seconds]] (≤6, e.g. audition_voices clips): cards that light up as each clip plays in turn, text? the line compare items [[label, image url or tc: handle], [label, image]], rows? [[metric, a, b]] (≤6, measured numbers, e.g. from look compare), text? layer handle "tc:layer/<scene id>" (bake_layer makes it, any project you can read): plays that scene's baked frames once, cap? a caption (no video render needed) code escape hatch: code "<body of function (box, tl, t, d, gsap)>" draws into the beat box with DOM/SVG/canvas and adds its tweens to tl at time t (≤3000 chars, d seconds). It runs only in a landed scene (call without live:true); the live page shows a placeholder. Use it for the one moment the data beats cannot say. kpi items [[label, value, delta?, tone?]] (≤4; numbers count up; tone ok|warn|bad) bars items [[label, number]], u? "%", max? (≤6, bars grow) list items [[text, status, detail?]] status ok|warn|bad|todo (≤5) steps items [[label, done|now|next]] horizontal progress (≤5) spark items [numbers], v? headline, label? trend line draws on table cols [str], rows [[cell…]] (≤5 rows reads well) note text, who? one big sentence or ask
GROW IT AS YOU THINK: call once with spec (title + first beat) so the viewer sees something at once, then append_beats with more beats; each call lands the whole scene again with the new beats added (about 4-7 s a call). live: true skips the landing and only stores the spec (about 1 s a call): the scene plays at /share/{token}/live (share_scene gives the token), which shows beats as they are appended. Finish with one call without live to land it as a normal scene. Exactly one of spec / append_beats. dry_run checks the spec for free; check: true looks at the landed scene. Free.
| Input | Type | Description |
|---|---|---|
scene_id required | string | The scene to put the report on (create_scene, or any scene whose composition you are replacing). |
spec | object | The whole report: {title?, sub?, accent?, light?, speed?, beats: [{k, h, items…}]}. Replaces what the scene has. |
append_beats | array | More beats for the report already on the scene. |
dry_run | boolean | Validate the spec and report the length without landing. Free. |
live | boolean | Store the spec only, no landing (about 1 s): plays at /share/{token}/live and picks up appended beats. Land it with a final call without live. |
check | boolean | string | object | After landing, look at the scene and return the findings as check, so one call lands and verifies. true = frame sheet + motion filmstrip; or a look preset (quick | motion-check | layout-check | sync-check | player-check | dense-motion); or {preset?, show: ["frames","motion","sync","player","sound"], frames?, fps?}. Fix what it flags with edits. |
put_scene_composition
Land a composition you wrote
Land a composition YOU authored onto a scene's element, through the same gates, scoping, theme injection and hosting a generated one takes. Read get_authoring_contract (shell: what is accepted, refused and warned about) and author against the values get_authoring_context gave you; a number you calculated instead of declaring is the most common reason a scene comes out wrong.
SEND ordinary HyperFrames: a full document (one root with data-composition-id and a paused GSAP timeline on window.__timelines[<id>]), a <template> sub-comp or the bare root. Large: upload it (create_upload) and pass src_url. Shared code: put_module, then link it with <script data-tc-module="kit:globe@2"></script>; modules in the answer lists what was linked.
Refusals cost nothing and carry the gate's words, so dry_run:true first is free. Expect: an unclosed <template>, no registered timeline, nodes appended to document.body. requestAnimationFrame or wall-clock motion lands with a WARNING and renders FROZEN (the renderer seeks a paused timeline); preview_composition with determinism judges it. The answer carries cost (bytes, class, lints with their fix) and revision. Refuses a scene mid-generation, a scene that changed while yours was in flight, and a landing that moves a module back to an older version (see if_revision). Free unless the HTML declares generated assets.
VOICE: <audio> is NOT mixed. Give the scene speech clips (one per line, its own voice_id, at on the scene clock); text lines are spoken on landing (SPENDS CREDITS, cached) and quoted by dry_run. Omit to keep the scene's; [] removes it.
PICTURES: <img data-gen="what it shows" data-gen-ratio="16:9"> is made on landing (SPENDS CREDITS, one batch; dry_run prices it) and baked as src + data-gen-key. Found again by key, so re-landing, even without src, is free and changing one prompt re-makes only that picture. The result's images says which were kept, reused or made.
FIXING WHAT YOU LANDED: send edits ([{find, replace}], each find matching once) instead of html; the server patches the source you sent last time (get_composition_source authored:true) and lands it through every gate. Refused, nothing written, if a find misses or the scene changed since: then send the full html.
async: true answers at once with a job_id; jobs {id} reads the result (no 60 s timeout).
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id (from get_storyboard or get_inventory). |
html | string | The composition source: a full HyperFrames document, a <template> sub-comp, or the bare root. Max 2,000,000 characters. Pass exactly one of html / src_url. |
src_url | string | An https URL to fetch the composition from instead of html (the worker fetches it; private and internal addresses are refused). Use it for documents too large to inline. |
edits | array | Instead of html: patch the source you sent at the last landing. Applied in order; each find must match exactly once. |
element_id | string | Which composition element. Omit only when the scene has exactly one; a scene with several refuses rather than guessing, and names them. |
dry_run | boolean | Validate and report without hosting or writing. Free — do this first. |
speech | array | Speech placed by the scene, one clip per line. source: {text, voice_id|clone_id} (spoken on landing, SPENDS CREDITS, cached), {asset_id} (a speak result) or {media_id, from?, to?} (a slice of a transcribed recording). at: seconds | "bar:3" | "beat:5" | "after:<id>+400"; omit to follow the previous clip. The scene lasts as long as its speech (+0.6s) unless it has a planned duration (update_scene {duration}, or a plan clock other than speech:true), and overrunning that is refused. cuts / fx: typecut://studio/guide "Voice inside the scene". |
skill | string | The skill (slug or slug@version) this scene is authored with; recorded on the scene so get_scene_reference and copy_scenes know its look. Default: the scene's effective style skill (get_project_skill with scene_id). |
as_take | boolean | Land as a DRAFT TAKE: same gates, mount and hosting, written to a new scene take instead of the scene, which stays exactly as it is. Iterate on a scene someone is watching without touching it; preview the draft with preview_composition src_url, then scene_takes switch to make it live. |
take_label | string | With as_take: a short name for the take. |
take_note | string | With as_take: why it exists / what it tries. |
if_revision | string | The scene revision from your last read or landing. Refused if another writer landed since. Also confirms a landing that moves a module back to an older version, which is otherwise refused. |
check | boolean | string | object | After landing, look at the scene and return the findings as check, so one call lands and verifies. true = frame sheet + motion filmstrip; or a look preset (quick | motion-check | layout-check | sync-check | player-check | dense-motion); or {preset?, show: ["frames","motion","sync","player","sound"], frames?, fps?}. Fix what it flags with edits. |
async | boolean | Start the call as a job and answer at once with {job_id}. Read it with jobs {id, wait_sec: 50}: the same result this call returns. Use it whenever the call may take more than about 30 s. |
idempotency_key | string | With async: any string unique to this call. A retry with the same key joins the job already started instead of starting another. |
put_scene_compositions
Land several authored compositions
put_scene_composition for up to 20 scenes in ONE call: each item is landed exactly as put_scene_composition would (same gates, same speech rules), and every verdict comes back inline, in order. One refused item does not stop the others. dry_run: true checks them all for free. SPENDS CREDITS only for speech text lines not yet spoken (see put_scene_composition).
MANY SCENES, ONE DESIGN: send template once (a full document with {{key}} slots, HTML-escaped, or {{{key}}} raw) and per scene data ({key: value}) instead of html. check:true looks at every landed scene and returns the findings with the verdicts. Over about 6 scenes, add async:true.
async: true answers at once with a job_id; jobs {id} reads the result (no 60 s timeout).
| Input | Type | Description |
|---|---|---|
scenes required | array | One per scene: {scene_id, html | src_url | edits | data, element_id?, speech?, skill?} — the fields of put_scene_composition. |
template | string | One document for every scene that sends data: {{key}} is filled HTML-escaped, {{{key}}} raw. |
dry_run | boolean | Validate and report without hosting or writing. Free. |
check | boolean | string | object | After landing, look at the scene and return the findings as check, so one call lands and verifies. true = frame sheet + motion filmstrip; or a look preset (quick | motion-check | layout-check | sync-check | player-check | dense-motion); or {preset?, show: ["frames","motion","sync","player","sound"], frames?, fps?}. Fix what it flags with edits. |
async | boolean | Start the call as a job and answer at once with {job_id}. Read it with jobs {id, wait_sec: 50}: the same result this call returns. Use it whenever the call may take more than about 30 s. |
idempotency_key | string | With async: any string unique to this call. A retry with the same key joins the job already started instead of starting another. |
put_skill
Publish a skill version
Staff only. Publish a new immutable version of a skill: title, summary, the SKILL.md (body_md) and the structured record (when_to_use, kits, stub, vocabulary, recipe, rules, image_recipes, pacing, checks, theme_slot). Kits you host are resolved to pin tags. STATUS-ONLY: send just {slug, version, status} (no title/body_md/record) to move an existing version draft ⇄ active → deprecated without re-sending its content; a full re-put with byte-identical content does the same, a differing body is a 409. Readers are served the highest-numbered ACTIVE version (promoting never demotes another). dry_run validates. Free.
| Input | Type | Description |
|---|---|---|
slug required | string | Lowercase slug, e.g. "history-map". |
version | number | Default: latest + 1. |
status | "draft" | "active" | "deprecated" | Default active. |
title | string | One-line name. Required for a new version; omit for a status-only call. |
summary | string | One sentence for list_skills. |
body_md | string | The whole SKILL.md. |
record | object | The structured record (see description). |
dry_run | boolean | Validate only. |
put_skill_exemplars
Curate a skill's worked shots
Staff only. Upsert worked shots of a skill by n: {n, title, brief, intents[], duration_s, code, words[], state{entry,exit}, frames[{t,url}], provenance}. status curated (default) serves them; draft hides them; retired removes them. replace:true retires every exemplar not in the list. Free.
| Input | Type | Description |
|---|---|---|
skill required | string | The skill slug. |
exemplars required | array | The exemplars (≤60 per call). |
replace | boolean | Retire the skill's exemplars not in this list. |
read_composition_unit
Zoom into one composition unit
Read ONE unit of a gen:html composition in full: its markup (outerHTML), the CSS rules that mention it, the timeline statements that animate it, its parent chain and sibling keys. A few hundred tokens instead of the whole document — use it AFTER inspect_composition to decide whether a change is additive (a sibling-shaped new unit) or a rewrite of this unit, and to quote exact text for a precise edit instruction. Read-only, free.
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id. |
unit_key required | string | Unit key from inspect_composition. |
element_id | string | Optional composition element filter (when a scene mounts several). |
refresh_scenes
Refresh copied scenes from their source
A scene made by copy_scenes remembers the scene it came from (metadata.copied_from). refresh_scenes pulls that source's CURRENT composition, look and sound into the copy, so a scene reused across projects follows its source on request. The copy keeps its own title, place, narration link and length. Only copies whose source has changed are touched (status refreshed | current | no_source). Never automatic. Undo like any scene edit (undo_scene_edit). dry_run:true reports which are behind. Free.
| Input | Type | Description |
|---|---|---|
scene_ids required | array | The copied scenes to refresh. |
include_sound | boolean | Pull the source's music and sfx too. Default true. |
dry_run | boolean | Report which are behind; write nothing. |
regenerate_elements
Re-make a scene's generated elements
Re-make the generated elements of an ALREADY-RESOLVED scene, keeping its scene source. SPENDS CREDITS — send dry_run true first to see which elements would be re-made and what that costs, and show the user the number before you spend it.
This is the call that actually dispatches the worker, and nothing else does it for an existing scene: edit_elements rewrites the source and recompiles, but a gen element it touched stays unresolved until something re-makes it; resolve_project skips the scene, because that gates on scenes never resolved at all; and generate_scene on an existing scene throws the edited source away and re-authors from the prompt. So reach for this when you have edited a gen:html / gen:image line and the scene still shows the old picture, or when a composer-level fix has landed and the already-generated scenes have to pick it up.
Name element_ids to keep a repair surgical — omitted, every resolved gen element in the scene is re-made and re-charged. Asynchronous: poll get_scene or get_project_status rather than assuming it landed.
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id. |
element_ids | array | Element ids to re-make (from get_scene or inspect_composition). Omit to re-make every resolved gen element in the scene, which costs more — prefer naming them. |
dry_run | boolean | True = report which elements would be re-made and the cost, and change nothing. Do this first. |
release_scene
Let go of a scene you claimed
Drop your claim on a scene (the lock clears for everyone). Idempotent: releasing a claim you do not hold (or that already lapsed) is fine and returns released:false. Free.
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id. |
repin_modules
Bring a project's scenes onto its runtime's module versions
After a module bump (put_module of kit3d@12, then set_project_runtime to move the project to it), the scenes already landed still carry the old version until they are landed again. This does that loop: for every scene of the project whose composition carries a module at a different version than its runtime lists, it takes the authored source kept at the last landing, rewrites any explicit pin of that module to the runtime's, and lands it again through the ordinary gates. A scene with no kept source, changed since, or opted out (data-tc-runtime="none") is reported with the reason and left alone. ALWAYS dry_run:true first. Also the way a theme set after landing reaches scenes you authored: refresh_theme:true. Free; every land is in the History panel.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
dry_run | boolean | true = report which scenes would be repinned (each re-validated by the worker) and write nothing. |
refresh_theme | boolean | true = also re-land every scene you authored, module-stale or not, so a theme set (set_theme) AFTER landing reaches it: the theme's CSS variables (--font-display, colour roles) are injected at land, so an authored scene keeps the look it was landed with. |
report_friction
Report friction using Typecut
Tell the Typecut team what cost you extra work: a call that failed or needed retries, a result too big to read, a task that took many round trips, information you had to fetch in several calls. OPTIONAL. Offer it once at the end of a task or when you hit a real wall, never mid-task, and only for friction you actually met doing real work (not preferences or redesigns). observed is a fact: what happened, which tool, the numbers. suggested is an optional idea, stored apart because it may be wrong. At most 3 per session; after that the call is answered but not stored. Each report becomes an issue in an inbox that a watcher works through (issues); it never comes back to you as a task. Free.
| Input | Type | Description |
|---|---|---|
observed required | string | What happened, one or two lines, with the tool name and numbers (calls, KB, error). Required. |
suggested | string | Optional: what would have helped. Kept separate from observed. |
tool | string | The tool it concerns, if one. |
model_tier | "opus" | "sonnet" | "haiku" | "other" | Your model tier. |
cost | object | Numbers only, e.g. {"calls":9,"kb":72}. |
project_id | string | The project you were working in, if any. |
kind | "gap" | "bug" | "slow" | "missing_tool" | "docs" | gap: something you needed that Typecut does not offer; bug; slow; missing_tool; docs: a guide or description that misled you. |
resync_compositions
Re-derive a project's compositions from their scene source
Recompile every scene in a project from its own scene source and store the result. A scene's stored composition is what every surface renders — editor, scene player, export — and it is written ONCE, at save. So a compiler fix only ever applies to the next save, and a project made before it keeps whatever the old compiler produced. This is how a fix reaches scenes that already exist. ALWAYS run dry_run:true first and read the per-element changes: the scene source is the truth and the compile is deterministic, so this is a recomputation rather than an edit, but it rewrites whole compositions. A scene whose compile fails is reported and left untouched. Free, and every write is recoverable from the History panel.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
dry_run | boolean | true = report what would change and write nothing. Do this first. |
save_recipe
Save the model choices that worked as a recipe
Free. Save what you settled on so it can be re-applied to a later project or by another agent: steps [{tool: "generate_image"|"generate_video", model, params, fallbacks (up to 3, same tool), note}]. Models must be ids from list_catalog("models", tool). params are the method (aspect, duration), never the prompt or an image url. Saving an existing name replaces it; pass fork_of to copy a recipe under a new name (steps optional then, to change it).
| Input | Type | Description |
|---|---|---|
name required | string | 2 to 60 characters, unique on the account. |
description | string | What it is for, one or two sentences. |
steps | array | The ordered steps. |
fork_of | string | Name of an existing recipe to copy. |
scene_takes
Save, list or switch the versions of a scene
Keep alternative versions ("takes") of ONE scene and switch between them: try a different look or treatment without losing the one you have. action "save" freezes the scene as it is now (give it a label, e.g. "dark, big type"); "list" (default) shows the saved takes and which one the scene is right now; "switch" with take_id makes that take the scene. Switching first freezes the current state if it matches no take, so nothing is ever lost. A scene anchored to narration keeps its length and place (the voice and timeline do not move); a free scene takes the take's length. Workflow for variants: save the current one, edit the scene (put_scene_composition / edit_elements), save again, then compare_takes and switch. Free.
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id. |
action | "list" | "save" | "switch" | Default "list". |
label | string | save: a short name for this version. |
note | string | save: why it exists / what it tries. |
take_id | string | switch: the take to make the scene. |
set_project_skill
Choose a project skill
Add, replace or clear a skill of the project. skill is a slug or slug@version from list_skills (active ones; staff also draft); null clears the entry at that role + scope. role (default style): "style" = the look, ONE per scope; "recipe" = how the film is made, ONE per scope, in force for the whole project and sitting on top of a style; "cast" = a character skill shared across scenes, one per slug. scope (default the whole project): {chapter_id} or {scene_id} makes a style apply to that chapter or scene only (scene beats chapter beats project; skill tokens are prefixed per skill, so two never collide). To drop ONE cast of several send skill:null with ref. A style at project scope is the original single skill: optional palette (role → hex) and type (role → family) override its tokens, theme_slug seeds them from a registry theme, and it is mirrored into the bible look (look.skill). note is a hint for the author. Existing scenes do not change until regenerated. Free. Needs the skills look flag on the account.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
skill | string | Skill slug or slug@version; omit or send null to clear. |
role | string | "style" (default), "recipe" or "cast". |
scope | object | Where it applies: omit for the whole project, or {"chapter_id":"…"} or {"scene_id":"…"}. |
ref | string | With skill null on a cast: clear only this one. |
note | string | A hint for the author, e.g. which structure a recipe follows. Max 240 characters. |
theme_slug | string | Style only: take palette and type from this registry theme. |
palette | object | Style only: role → hex overrides, e.g. {"bg":"#0b0b10","accent":"#ff5a36"}. These win over the theme. |
type | object | Style only: role → font family overrides, e.g. {"display":"Fraunces"}. |
set_scene_frame
Set a scene's background or canvas
Set what sits BEHIND a scene's composition and the frame it renders in, without touching the composition. background: {type:"solid", color:"#0b0b12"} | {media_url, fit:"cover"|"contain"} (image or video by extension; is_video to force) | {type:"stage"} to drop it so the theme's stage shows. canvas: "WxH" gives this ONE scene its own frame (e.g. "1080x1920"); elements re-resolve against it. Re-transpiled like an element edit. Free.
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id. |
background | object | {type:"solid", color} | {media_url, fit?, is_video?} | {type:"stage"}. |
canvas | string | "WxH" in pixels, e.g. "1080x1920". |
expected_version | number | composition_version from get_scene; a moved scene is a conflict, not a clobber. |
set_theme
Switch or restyle the theme
Either switch to an existing theme (slug, from list_catalog("themes")), or restyle the current one (colors/fonts), which forks it into a private copy rather than mutating a shared preset. Free. IMPORTANT: themes are baked into each scene when it is generated, so already-generated scenes keep their old look until regenerated — say so rather than promising a change the user will not see.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
slug | string | Switch to this theme slug. Mutually exclusive with colors/fonts. |
colors | object | Restyle: role → hex, e.g. {"accent":"#ff3300"}. Roles from get_theme. |
fonts | object | Restyle: role (display/heading/body/label/mono) → published font family. |
name | string | Name for the forked theme when restyling. |
expected_version | integer | The project's edit_version (from get_project or the last project-level write's response). Optional: when sent, the write is refused with a conflict (current_version, last_writer) if anyone changed the project settings since — nothing is written. Omit to write unconditionally. |
set_transition
Set the transition on one cut
Set — or clear — the transition on the cut BEFORE a scene. A seam is named by its INCOMING scene, which is how the timeline stores it and how the editor addresses it. On a chapter's first scene the seam is the CHAPTER JOIN, and only a geometric transition (render_mode "geometric") can cross it; the project's first scene has no seam. Slugs come from list_catalog("transitions"); anything else is refused rather than rendering as a plain cross-dissolve. transition:"none" makes it a hard cut. Leave duration_ms out to take the transition's own default, which is usually right — a textural transition (ink, liquid, pixelate) needs materially longer than a crossfade before it reads at all. This is the per-seam control; create_project's transitions list only cycles slugs across every boundary at plan time. To change the cut at the END of a scene, pass the NEXT scene's id. Free, and it changes nothing else about the scene.
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene the transition runs INTO (from get_storyboard). The seam is the cut before it. |
transition | string | Transition slug from list_catalog("transitions"), or "none" for a hard cut. Omit to change only the sound. |
duration_ms | number | Seam length in milliseconds (100–4000). Omit for the transition's own default. |
length | string | Seam length in MUSIC on the project tempo: "1/16", "1/8", "1/4", "1/2", "2 beats", "1 bar" (max 2 bars). Wins over duration_ms. The picture window is centred on the cut, so a hit lands on the downbeat. |
sfx_mode | string | Transition sound: "auto" (default), "always", or "never". |
sound | string | A synthesized sound for this cut: stutter-build, suck-hit, reverse-cymbal, boom, braam, riser-drop, downlifter, snare-build, stinger, shimmer, sub-drop, scratch, zap, shutter, swell, chapter — or "none". Builds lead INTO the cut, hits sit ON it; pitched parts follow the project key. |
sound_energy | string | "subtle", "medium" (default) or "strong". |
lead | string | How long a build runs before the cut: seconds ("1.5") or musical ("1 bar", "2 beats"). Default 1 bar on a tempo, else 1.6 s. |
split_scene
Split a scene in two
Cut one scene into two at a moment: at in seconds on the scene clock, or "word:<word>" — the word the second scene starts on. A scene anchored in narration keeps its voice: the second scene is anchored at that word in the same block and each takes its own slice of the voice (seconds snap to the nearest word). Lengths sum to the original. The composition is CLONED as is onto the second scene, so both play the full original design at their new lengths — re-author either one (put_scene_composition / edit_elements) afterwards. Runs as one changeset: undo_changes with the returned changeset_id reverts it. dry_run:true projects it without writing. Free.
| Input | Type | Description |
|---|---|---|
scene_id required | string | The scene to split. |
at required | number | string | Seconds (e.g. 3.5), or "word:<word>" for a scene anchored in narration. |
dry_run | boolean | Project only; nothing written. |
submit_composition
Submit an author-written scene script
Submit a scene script you wrote YOURSELF as a new standalone scene, and get a share link back. SPENDS CREDITS — it resolves the gen elements in the script.
This is the raw-script door for a STANDALONE scene. Plain HTML you wrote for a scene in a project goes through put_scene_composition instead — that is the main authoring door. Use this one for scripts built outside the composer: a PSD import, a hand-authored gen:html("…", src:"…") sub-comp, a script lifted from a lab.
The src: binding may be an http(s) url or a data: url: the resolver fetches it and re-hosts it, so a self-contained sub-comp needs no hosting of its own. Returns the new scene id and a share_url pointing at that ONE scene — not a whole project, which is what makes it the right way to look at a single composition.
Prefer generate_scene when you want Typecut to DESIGN something from a brief. Reach for this only when you already have the script and want it rendered as written.
The scene PLAYS in the conversation where the host renders MCP Apps views, so you can see what you authored rather than reasoning about it from the source.
| Input | Type | Description |
|---|---|---|
workspace_id | string | Workspace to create the scene in. Required for account-level keys; workspace-scoped keys fill it in. |
width | number | Canvas width in px. Defaults to the script's own canvas: line. |
height | number | Canvas height in px. |
fps | number | Frames per second. Default 30. |
duration | number | Scene duration in seconds. Defaults to the script's duration: — or, when it has none and the scene speaks, the speech length. |
aspect_ratio | string | e.g. "16:9". Alternative to width/height. |
speech | array | Speech placed by the scene, one clip per line. source: {text, voice_id|clone_id} (spoken on landing, SPENDS CREDITS, cached), {asset_id} (a speak result) or {media_id, from?, to?} (a slice of a transcribed recording). at: seconds | "bar:3" | "beat:5" | "after:<id>+400"; omit to follow the previous clip. The scene lasts as long as its speech (+0.6s) unless it has a planned duration (update_scene {duration}, or a plan clock other than speech:true), and overrunning that is refused. cuts / fx: typecut://studio/guide "Voice inside the scene". |
music | object |
undo_changes
Undo a changeset
Undo one list_changes entry as a whole (free). A changeset: only the most recent standing one can be undone directly, and the undo is itself a changeset. An edits entry (one API/MCP call): what it replaced is written back; if those rows changed since, it refuses with the stale rows — pass force: true to revert anyway.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
changeset_id required | string | A changeset id or an edits entry id, from apply_changes or list_changes. |
force | boolean | Edits entries only: revert even though the rows changed since. |
undo_scene_edit
Undo a scene edit
Roll a scene back: the answer to "undo that" / "I liked it better before". Restores the state captured just before the scene's most recent edit (steps rewinds further), or one exact restore point (revision_id from get_revisions). Covers EVERY kind of scene edit, including edit_elements, edit_composition_unit and llm_edit, which undo_changes cannot reach. Free and non-destructive: the rollback is itself recorded, so it can be undone too.
| Input | Type | Description |
|---|---|---|
scene_id required | string | The scene to roll back. |
steps | number | How many edits to rewind (1-10, default 1). |
revision_id | string | Restore this exact revision of the scene instead of counting steps. |
update_scene
Update a scene
Change a scene's own fields: title, prompt, duration, caption overrides, the scene's brand-mark motion. Free — no generation. To change what is IN the composition: a scene you wrote is re-landed with put_scene_composition; a scene Typecut designed is patched with edit_elements (it takes an expected_version, so it cannot silently overwrite someone editing the same scene).
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id. |
title | string | New title. |
prompt | string | New creative prompt (used when the scene is regenerated). |
duration | number | Scene duration in seconds. |
captions_enabled | boolean | Per-scene caption override. Omit to inherit the project default. |
caption_style_id | string | Per-scene caption look, from list_catalog("caption-styles"). Omit to inherit the project look; null to drop back to it. |
caption_style | object | Per-scene caption fine-tune — the same CaptionStyle partial set_captions takes, and the TOP layer of the cascade (preset < theme < project < scene). Deep-merged; null clears it. Read the current one with get_scene before patching. |
brand_formula | string | How the BRAND MARK moves in this scene — a surface-formula slug from list_formulas ("corner", "pulse", "wave", or an authored one). The last step of the cascade: omit to leave it, null to inherit the project's. A formula that follows a measured channel (music, voice) reads that scene's own audio window, so the same slug is a different motion in every scene. Requires a plan with custom_brand_mark; free films carry the Typecut mark and ignore it. |
pacing | string | How much MOTION this scene should carry, independent of what it depicts: "dense" for an opening that has to earn attention, "standard", or "held" for a scene that should stay legible while the narration does the work. It sets the density the composition author is asked for and the threshold under which a thin draft is retried — so "hold this one" is a real instruction rather than a note in the brief. Takes effect the next time the scene is generated. Omit to leave it; null to clear it back to the project default. |