Skip to content

Video tools

create_video_from_script

Create a narrated video from a script: synthesizes an AI voiceover, runs the b-roll/captions/graphics pipeline, and renders the final mp4, all in the background. Safe to call while the script is still generating; it waits for the text. Returns a job_id immediately: poll get_generation_status until status is "rendered" and video_url is set (typically a few minutes; "planned" means the cut is assembled and the encode is next). Settings omitted fall back to the user's saved defaults. This spends the user's credits. Confirm before calling.

ParameterTypeDescription
script_id requiredstring
aspect_ratiosstring[]Output aspect ratios, e.g. ["9:16"] for TikTok/Reels, ["16:9"] for YouTube/LinkedIn.
video_typestring"long-form" for one video, "both" to also cut short clips. One of: long-form, both.
clipsobjectShort-clip settings (only meaningful with video_type=both).
captions_enabledbooleanBurn in captions (default true).
caption_presetstringName or id of one of the user's saved caption presets.
brand_kitstringName or id of one of the user's saved brand kits (logos + graphics theme), or "editory" for Editory's own look (black, red and cream, no logo).
broll_enabledbooleanLayer stock b-roll under the narration.
broll_sourcestringStock footage source: pexels or pixabay.
broll_custom_filesobject[]The user's own footage/stills to cut away to, layered in alongside (or instead of) stock. Clips the user uploaded in this conversation are attached automatically; only pass this to override that set.
use_article_imagesbooleanWhether to cut to the photos published with the source article. Defaults to true: they are attached automatically, so only pass false when the user asks for stock footage only.
video_contextstringExtra context for the editing agents (topic, vibe, audience).
music_enabledbooleanLay a background music bed under the video.
music_stylestringThe feel of the music, in the user's own words ("upbeat", "cinematic and tense", "calm lo-fi"). Pass this whenever they say what they want. Without it the mood is inferred from the transcript, which ignores what they asked for, and on a video with no speech there is nothing to infer from.
sfx_enabledbooleanPunctuate the cut with sound effects (whooshes, impacts), placed against the transcript. Turning this on turns music on too unless music_enabled says otherwise; they are one lane in the pipeline.
graphics_enabledbooleanAnimated graphics overlays (quote cards, stats).
jump_cuts_enabledbooleanTighten the cut by removing pauses. Pass false to leave the pacing exactly as recorded.
voice_idstringElevenLabs voice id for the narration (see list_voices).

Returns: The cut is assembled and the mp4 rendered in the background: "planned" means the cut is built and the encode is next, "rendered" means the file is ready at video_url.

FieldTypeDescription
card always"video_job"What kind of result this is.
job_id alwaysstringPass this to get_generation_status, get_video_edit_state and edit_video.
status alwaysstringLifecycle status. Not a closed set; treat an unfamiliar value as in progress and re-check.
script_idstringThe script this video is being built from.
plan_onlybooleanFalse on this door: the pipeline continues past "planned" to a rendered mp4. When true, "planned" is the finish line rather than a stage on the way to a file.
applied_configobjectThe settings actually used, after the user's defaults filled in whatever you left out.
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.
  1. generate_script → script_id

  2. create_video_from_script → job_id

    You can call this while the script is still generating; it waits for the text rather than failing.

  3. get_generation_status until the job reports planned

  4. get_video_edit_state → the ids you need to change anything

  5. edit_video → apply the changes

create_video_from_upload

Edit a video the user attached in this conversation: runs the same pipeline as create_video_from_script over their own footage, with whichever layers they asked for. It can also put an AI NARRATION on the footage (narration_lines + voice_id): each line is voiced and placed at its time, sped up slightly or pushed later if it would overlap the next. That is the ONLY way to add a voice to an uploaded video; edit_video cannot. THIS is how you act on an attached video: an attachment is the subject of the request, not b-roll for some other cut, and you must never apply a request about it to an existing job found via list_recent. Their video already looks how they want, so ONLY the layers you name are turned on; everything else (captions, brand kit, b-roll, graphics, jump cuts) stays off, whatever their saved defaults say. Name every layer they asked for, and pass music_style whenever they describe a vibe. Stops at a previewable cut like create_video_from_script; it does NOT render a file. Confirm with the user before calling.

ParameterTypeDescription
upload_namestringFilename of the attached video to work on. Omit when the conversation holds exactly one video; with several, ask the user rather than guessing.
source_job_idstringInstead of an attachment, work on the ORIGINAL footage behind an existing video (its job id). This builds a NEW video from that footage; edits made to the existing cut do not carry over. Use it to put an AI narration on a video that has none.
aspect_ratiosstring[]Output aspect ratios, e.g. ["9:16"] for TikTok/Reels, ["16:9"] for YouTube/LinkedIn.
video_typestring"long-form" for one video, "both" to also cut short clips. One of: long-form, both.
clipsobjectShort-clip settings (only meaningful with video_type=both).
captions_enabledbooleanBurn in captions (default true).
caption_presetstringName or id of one of the user's saved caption presets.
brand_kitstringName or id of one of the user's saved brand kits (logos + graphics theme), or "editory" for Editory's own look (black, red and cream, no logo).
broll_enabledbooleanLayer stock b-roll under the narration.
broll_sourcestringStock footage source: pexels or pixabay.
broll_custom_filesobject[]The user's own footage/stills to cut away to, layered in alongside (or instead of) stock. Clips the user uploaded in this conversation are attached automatically; only pass this to override that set.
video_contextstringExtra context for the editing agents (topic, vibe, audience).
music_enabledbooleanLay a background music bed under the video.
music_stylestringThe feel of the music, in the user's own words ("upbeat", "cinematic and tense", "calm lo-fi"). Pass this whenever they say what they want. Without it the mood is inferred from the transcript, which ignores what they asked for, and on a video with no speech there is nothing to infer from.
sfx_enabledbooleanPunctuate the cut with sound effects (whooshes, impacts), placed against the transcript. Turning this on turns music on too unless music_enabled says otherwise; they are one lane in the pipeline.
graphics_enabledbooleanAnimated graphics overlays (quote cards, stats).
jump_cuts_enabledbooleanTighten the cut by removing pauses. Pass false to leave the pacing exactly as recorded.
narration_linesobject[]AI narration to lay over the video, one entry per line in speaking order. Read the user's script in WHATEVER notation they used ([1.9], 0:02, "at 4s", "2 seconds in", a table, or no times at all) and copy their words EXACTLY: never reword, fix or shorten them. start_seconds is when the line should begin, from the start of the video; leave it out for a line with no time and it follows the previous line. If they asked for narration but gave no words, call watch_video, draft lines timed to what it saw, show them, and only pass them here after the user approves.
voice_idstringThe narrator, from list_voices: its id (or exact name). Required with narration_lines, and it is the USER's choice: show them list_voices and ask unless they already named one. Pass "default" only when they said to use their default / any voice.
original_audiostringWith narration_lines, what happens to the video's own sound: keep it quietly under the voice, or mute it. Only needed when the video has sound; the tool tells you when it does, and then you ask the user. One of: keep, mute.

Returns: Same background assembly and the same "planned" finish line as create_video_from_script, over a video the user attached instead of a synthesized narration.

FieldTypeDescription
card always"video_job"What kind of result this is.
job_id alwaysstringPass this to get_generation_status, get_video_edit_state and edit_video.
status alwaysstringLifecycle status. Not a closed set; treat an unfamiliar value as in progress and re-check.
plan_onlybooleanTrue here, and it is why "planned" is the finish line rather than a stage on the way to a file.
source_namestring | nullThe attached file this cut was built from. Say it back so the user can confirm it was the right one.
source_duration_secondsnumber | nullThe upload's measured length, when the tray probed it.
applied_configobjectThe settings actually used, after the user's defaults filled in whatever you left out.
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.

The other way in: the user already has the footage, and wants it cut rather than narrated. Same pipeline, same planned finish line, same editing tools afterwards. The only difference is that the video comes from a file instead of a synthesized voiceover, so no script is involved.

Unlike create_video_from_script, this does not fall back to the user’s saved defaults for layers you leave unset. Their video already looks how they want it; captions, brand kit, b-roll, graphics and jump cuts all stay off unless you ask for them. So “add music and sound effects” is exactly this:

{
"music_enabled": true,
"sfx_enabled": true,
"music_style": "upbeat"
}

Pass music_style whenever the user describes a vibe. Without it the mood is inferred from the transcript, which discards what they asked for, and on a video with no speech has nothing to infer from at all.

This is the only tool that can put a voice on an uploaded video; edit_video cannot. Pass narration_lines (one entry per line, in speaking order) and a voice_id:

{
"narration_lines": [
{ "text": "This news video took five minutes.", "start_seconds": 0 },
{ "text": "You just hit record.", "start_seconds": 1.9 },
{ "text": "Editory does the edit." }
],
"voice_id": "George",
"music_enabled": true
}
  • Read the user’s notation, keep their words. [1.9], 0:02, “at four seconds” and a plain list all become start_seconds; a line with no time follows the one before it. The text is spoken exactly as passed.
  • The voice is the user’s choice. Without voice_id the call returns voice_required; show them list_voices and ask. "default" means their saved voice.
  • If the video has its own sound the call returns original_audio_choice_required until you pass original_audio: "keep" (under the voice, quietly) or "mute".
  • Lines never overlap. A line longer than the gap before the next is sped up, at most 1.2×, then moved later if it still does not fit; if the last line runs past the end, the final frame is held. The job reports which lines moved.

Each line is voiced separately, so the job’s progress counts lines (narration:3/9) before the ordinary pipeline stages begin.

watch_video

Look at a video the user attached: returns a timestamped list of beats (what is on screen, and any words shown on screen) plus whether it has its own sound. Use it before writing narration for a video when the user gave no words, so every line is timed to what is actually on screen, and whenever you need to know what an upload shows. Takes up to a minute.

ParameterTypeDescription
upload_namestringFilename of the attached video. Omit when the conversation holds exactly one video.
source_job_idstringInstead of an attachment, watch the original footage behind an existing video (its job id).

Returns

FieldTypeDescription
card always"watch"What kind of result this is.
beats alwaysobject[]What is on screen, in order, covering the whole video.
source_namestring | nullThe attached file that was watched.
duration_secondsnumber | nullThe video's length.
has_audiobooleanWhether the video has its own sound.
summarystring | nullOne sentence on the whole video.

A timestamped account of what an attached video shows: beats with what is on screen and any words visible, and whether it has its own sound. Use it to write narration when the user gave no words, so every line is timed to the picture, then show the draft and wait for a yes before generating audio.

get_generation_status

Batch-check progress of script generations and/or video jobs. Videos report stage + progress while running and the final video URL when rendered.

ParameterTypeDescription
script_idsstring[]
job_idsstring[]

Returns

FieldTypeDescription
card always"status"What kind of result this is.
scripts alwaysobject[]One row per script id asked about. An id that does not exist is simply absent.
videos alwaysobject[]One row per video job id asked about.

Takes both scripts and videos in one call, so a client tracking several things polls once rather than per item. Both lists are capped at 25.

list_recent

List the user's recent scripts or videos, for requests like "make a video from my last script". This SHOWS the user their work; it does not identify which video a request is about. Never treat the newest row as "this video".

ParameterTypeDescription
type requiredstringOne of: scripts, videos.
limitnumberMax rows (default 5, cap 20).

Returns: Carries scripts or videos, whichever was asked for, never both.

FieldTypeDescription
card always"status"What kind of result this is.
scriptsobject[]Present when scripts were asked for. Newest first.
videosobject[]Present when videos were asked for. Newest first.

Two things a script can become are not reachable over MCP, and no tool here will pretend otherwise:

  • Recording it yourself, on the teleprompter with the script loaded.
  • Putting it in a fixed layout: Split Screen, Cut-In, Rotoscoped. See the layout template reference for what each shape does.

Both need a camera or a decision only the person in front of it can make, so both start from the web app. Editory’s own in-app chat has tools that deep-link into those flows; they’re withheld over MCP because a link that opens Editory’s wizard is useless in a client that isn’t Editory.

The practical version: get the script written here, then open editory.news/videos/generate and pick it from the list. It’ll be there.