Skip to content

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.

ParameterTypeDescription
job_id requiredstringThe video job id.

Returns

FieldTypeDescription
card always"video_preview"What kind of result this is.
job_id alwaysstringThe video job. Pass it to edit_video and get_generation_status.
version_id alwaysstringThis snapshot in the video's edit history.
summary alwaysobjectThe cut's structure and ids, with no payloads. What edit_video's arguments refer to.
available_operations alwaysstring[]Every op name edit_video accepts.
versionnumberVersion number, counting from 1.
labelstring | nullThe short name given to this version.
titlestring | nullThe video's title.
aspect_ratiostring | nulle.g. "9:16".
duration_secondsnumberRuntime of this version.
is_currentbooleanWhether this is the version the job now holds.
job_statusstringLifecycle status. Not a closed set; treat an unfamiliar value as in progress and re-check.
linksobjectAbsolute 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.
notestringGuidance for you, not for the user. Act on it; do not read it out.
video_urlstring | nullThe rendered mp4, once one exists. Null on a planned cut, which is finished and watchable at links.video, not still processing.
thumbnail_urlstring | nullA still from the cut, where one has been made.
narrationobject | nullWhat 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_namestring | nullThe 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_uploadsobject[]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.

ParameterTypeDescription
job_id requiredstringThe video job id.
operations requiredobject[]The changes to apply, in order. Each entry names an `op` plus only the fields that op uses.
labelstringThree-to-five word name for this version, shown on the chat card, e.g. "bigger captions" or "no music".

Returns

FieldTypeDescription
card always"video_preview"What kind of result this is.
job_id alwaysstringThe video job. Pass it to edit_video and get_generation_status.
version_id alwaysstringThis snapshot in the video's edit history.
applied alwaysobject[]What actually changed. Summarize this in one line rather than repeating it.
versionnumberVersion number, counting from 1.
labelstring | nullThe short name given to this version.
titlestring | nullThe video's title.
aspect_ratiostring | nulle.g. "9:16".
duration_secondsnumberRuntime of this version.
is_currentbooleanWhether this is the version the job now holds.
job_statusstringLifecycle status. Not a closed set; treat an unfamiliar value as in progress and re-check.
linksobjectAbsolute 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.
notestringGuidance for you, not for the user. Act on it; do not read it out.
warningsstring[]Operations that matched nothing, and structural problems that predate the edit.
needs_renderbooleanThe chat preview already plays the edit; only the downloadable file needs a re-render, which the user triggers.

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.

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.

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.

ParameterTypeDescription
job_id requiredstringThe narrated video's job id.
narration_linesobject[]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_idstringA voice from list_voices (id or exact name), or "default". Omit to keep the voice the narration has now.
original_audiostringChange what happens to the footage's own sound. Omit to keep it as it is. One of: keep, mute.
labelstringThree-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.

FieldTypeDescription
card always"video_preview"What kind of result this is.
job_id alwaysstringThe video job. Pass it to edit_video and get_generation_status.
version_id alwaysstringThis snapshot in the video's edit history.
applied alwaysobject[]What changed: one entry for the re-voiced narration.
versionnumberVersion number, counting from 1.
labelstring | nullThe short name given to this version.
titlestring | nullThe video's title.
aspect_ratiostring | nulle.g. "9:16".
duration_secondsnumberRuntime of this version.
is_currentbooleanWhether this is the version the job now holds.
job_statusstringLifecycle status. Not a closed set; treat an unfamiliar value as in progress and re-check.
linksobjectAbsolute 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.
notestringGuidance for you, not for the user. Act on it; do not read it out.
warningsstring[]What else moved with the narration: a new length, rebuilt captions.
narrationobjectThe narration the cut has now.
needs_renderbooleanThe 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.