Editing tools
Editing is two tools: one that tells you what’s in the video, and one that changes it. Always call the first before the second: edits address items by id, and guessing an id gets you a warning instead of a change.
get_video_edit_state
Read a video cut's structure so you can change it: its tracks, the ids of every b-roll clip / graphic / music item, the caption style, and the graphics theme. On first access, this saves a non-destructive baseline snapshot for version history. Call this BEFORE edit_video: the operation arguments reference these ids. Also the way to show the user their video: the result renders as a playable preview in the chat.
| Parameter | Type | Description |
|---|---|---|
job_id required | string | The video job id. |
Returns
| Field | Type | Description |
|---|---|---|
card always | "video_preview" | What kind of result this is. |
job_id always | string | The video job. Pass it to edit_video and get_generation_status. |
version_id always | string | This snapshot in the video's edit history. |
summary always | object | The cut's structure and ids, with no payloads. What edit_video's arguments refer to. |
available_operations always | string[] | Every op name edit_video accepts. |
version | number | Version number, counting from 1. |
label | string | null | The short name given to this version. |
title | string | null | The video's title. |
aspect_ratio | string | null | e.g. "9:16". |
duration_seconds | number | Runtime of this version. |
is_current | boolean | Whether this is the version the job now holds. |
job_status | string | Lifecycle status. Not a closed set; treat an unfamiliar value as in progress and re-check. |
links | object | Absolute page URLs for what this result is about. Give these to the user: they are how a script gets read and a planned cut gets watched or rendered. |
note | string | Guidance for you, not for the user. Act on it; do not read it out. |
video_url | string | null | The rendered mp4, once one exists. Null on a planned cut, which is finished and watchable at links.video, not still processing. |
thumbnail_url | string | null | A still from the cut, where one has been made. |
narration | object | null | What the AI narration on a narrated upload says now, and in which voice. Null for any other video. Change it with redo_narration, never edit_video. |
source_name | string | null | The file this cut was built from. Check it against what the user is describing. If it doesn't match, you have the wrong video. |
available_uploads | object[] | Files the user dropped into this conversation. Reference one by name instead of a URL. |
Returns a compact summary rather than the whole manifest: the tracks, the item
and asset ids, the caption style and the graphics theme. That is everything
edit_video needs to address, without spending thousands of tokens on
frame-level caption timings.
edit_video
Change a video cut by editing its manifest: add or swap b-roll, music and sound effects, caption look/size/position, lanes on/off, mix levels, graphic copy, brand colours, transition style, retiming or removing an overlay. Adding footage takes two operations: add_asset to register the file, then add_item to place it (give each placement its own source_start_seconds so one long clip yields different shots). The chat preview updates immediately; the user does NOT need a re-render to see it (the downloadable file does, via the Render button on the card). Call get_video_edit_state first so your operations reference real ids: item ids and asset ids are different namespaces. Batch every change the user asked for into ONE call.
| Parameter | Type | Description |
|---|---|---|
job_id required | string | The video job id. |
operations required | object[] | The changes to apply, in order. Each entry names an `op` plus only the fields that op uses. |
label | string | Three-to-five word name for this version, shown on the chat card, e.g. "bigger captions" or "no music". |
Returns
| Field | Type | Description |
|---|---|---|
card always | "video_preview" | What kind of result this is. |
job_id always | string | The video job. Pass it to edit_video and get_generation_status. |
version_id always | string | This snapshot in the video's edit history. |
applied always | object[] | What actually changed. Summarize this in one line rather than repeating it. |
version | number | Version number, counting from 1. |
label | string | null | The short name given to this version. |
title | string | null | The video's title. |
aspect_ratio | string | null | e.g. "9:16". |
duration_seconds | number | Runtime of this version. |
is_current | boolean | Whether this is the version the job now holds. |
job_status | string | Lifecycle status. Not a closed set; treat an unfamiliar value as in progress and re-check. |
links | object | Absolute page URLs for what this result is about. Give these to the user: they are how a script gets read and a planned cut gets watched or rendered. |
note | string | Guidance for you, not for the user. Act on it; do not read it out. |
warnings | string[] | Operations that matched nothing, and structural problems that predate the edit. |
needs_render | boolean | The chat preview already plays the edit; only the downloadable file needs a re-render, which the user triggers. |
Operations, not JSON
Section titled “Operations, not JSON”The agent never writes manifest JSON. It emits named operations ({"op": "set_caption_style", "font_size": 64}), which are validated against the
registry and applied atomically. The full vocabulary is on its own page:
Four guarantees are worth designing around:
| Guarantee | What it means for you |
|---|---|
| Fail closed | If the video was valid before, it’s valid after. An edit that would break it writes nothing; there is no half-applied state to clean up. |
| A miss is a warning | An operation matching no item returns a warning naming the id that missed, not an error. The batch still applies. Read the warnings; they’re how you learn an id was wrong. |
| All-miss fails | If every operation matched nothing, the call fails outright rather than reporting a hollow success. |
| Versioned | Each edit writes a new version. Earlier cuts stay watchable. |
Targeting
Section titled “Targeting”By default an edit applies to every definition: the long form and every clip. That is almost always right, because clips are cut from the long form and a change to one should be in the other.
Pass target to narrow it: "long_form", or "clip:0" for the first clip.
Changing what a narration says
Section titled “Changing what a narration says”redo_narration
Change what the AI narration on a narrated upload SAYS, or the voice saying it, and keep everything else about the cut. Re-voices the lines over the user's original footage (about 30 seconds), swaps the new narration into the current cut and saves it as a new version on the same card; music, crops and edits stay, captions are rebuilt to the new words, and the length follows the new narration. This is the ONLY way to change spoken words: edit_video's replace_text changes on-screen text and leaves the voice saying the old line. Pass narration_lines as the COMPLETE revised list (every line, in order, with start_seconds where the line has a time), not just the changed ones; get_video_edit_state shows the current lines. Omit voice_id to keep the current voice.
| Parameter | Type | Description |
|---|---|---|
job_id required | string | The narrated video's job id. |
narration_lines | object[] | Every line the narration should speak, in order, with the user's changes applied word for word. Keep each unchanged line's text and start_seconds exactly as they were. |
voice_id | string | A voice from list_voices (id or exact name), or "default". Omit to keep the voice the narration has now. |
original_audio | string | Change what happens to the footage's own sound. Omit to keep it as it is. One of: keep, mute. |
label | string | Three-to-five word name for this version, e.g. "new opening line". |
Returns: A new version of the same cut with the narration re-voiced. Like edit_video, the chat preview already plays it; only the downloadable file needs a render.
| Field | Type | Description |
|---|---|---|
card always | "video_preview" | What kind of result this is. |
job_id always | string | The video job. Pass it to edit_video and get_generation_status. |
version_id always | string | This snapshot in the video's edit history. |
applied always | object[] | What changed: one entry for the re-voiced narration. |
version | number | Version number, counting from 1. |
label | string | null | The short name given to this version. |
title | string | null | The video's title. |
aspect_ratio | string | null | e.g. "9:16". |
duration_seconds | number | Runtime of this version. |
is_current | boolean | Whether this is the version the job now holds. |
job_status | string | Lifecycle status. Not a closed set; treat an unfamiliar value as in progress and re-check. |
links | object | Absolute page URLs for what this result is about. Give these to the user: they are how a script gets read and a planned cut gets watched or rendered. |
note | string | Guidance for you, not for the user. Act on it; do not read it out. |
warnings | string[] | What else moved with the narration: a new length, rebuilt captions. |
narration | object | The narration the cut has now. |
needs_render | boolean | The chat preview already plays it; only the downloadable file needs a re-render, which the user triggers. |
A narration added to an uploaded video (create_video_from_upload with
narration_lines) is mixed into the footage’s own sound, so edit_video
cannot change it: replace_text edits words on screen and the voice keeps
saying the old line. redo_narration re-voices the lines over the original
upload and swaps the result into the current cut as a new version of the same
video. Music, crops and other edits stay; captions are rebuilt to the new
words; the cut’s length follows the new narration.
Pass the complete revised list of lines (read the current ones from
get_video_edit_state’s narration.lines), and omit voice_id to keep the
voice. It takes about as long as the first narration did, roughly half a
minute for a short video, and uses no project from the monthly allowance.