Voice, music and sound
Narration with word timings, spoken lines, the music bed, the beat grid and the mix. 40 tools.
On the main list
The tools a client sees when it connects.
get_timeline
The film timeline — every track on one clock
The whole film on one clock: scene windows, tempo, voice, music, cues, SFX, transitions, captions. detail adds bars, beats, words; sync {} checks picture vs sound (guide). Free.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
chapter_id | string | Only this chapter (its window is the default detail range). |
detail | object | Expand a range: bars, beats, words, elements, each cue. |
sync | object | check_sync input (guide). |
voice_project
Voice the project
Voiceover and word timings for the project or one chapter; composes nothing. SPENDS CREDITS; dry_run prices it. Async.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
chapter_id | string | Only this chapter. |
dry_run | boolean | Price it, change nothing. |
include_stale | boolean | Also re-voice blocks whose text changed since. |
speak
Speak lines
Speak ≤20 lines into the library, each with word timings. SPENDS CREDITS (a repeated line in the same voice is free).
| Input | Type | Description |
|---|---|---|
lines required | array | Lines; each may override the voice. |
voice_id | string | Default voice (list_voices). |
clone_id | string | Default voice clone. |
language | string | Default language. |
set_music
Set the background music
Project music: track_id, url, generate {…} (SPENDS CREDITS), score {…} or Python code (free; guide "Score"). None = remove.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
track_id | string | Music track id from list_catalog("music"). |
url | string | Direct audio URL, if you are not using a catalog track. |
generate | object | |
score | object | |
code | string | |
dry_run | boolean | |
range | object |
Callable by name
Not listed on connect, to keep the list short. Call them directly by name, or find them with tools.
analyze_audio
Hear what is in a recording
Analyse an audio (or video) asset or a music track into data you can act on. DETECT: is there music, speech, singing, silence, and where (a timeline plus tags). For MUSIC: tempo and the TRACKED beats and bars (downbeats), drum hits (kick / snare / hat, with accent), bass and lead notes, the key, SECTIONS with their energy, and the SALIENT moments (drops, fills, entries). result.condensed is a short readable summary; result.features_url is the full event file the composition kernels read.
SOUND CAPTIONS (music, a few minutes after "ready"): every strike as a record you can move with — its level, attack, decay, pitch and glide, brightness — plus ratchets / rolls / flams as ONE gesture each (strokes, rate, speeding up or slowing) and the phrase landings where the stems converge (with the phrase length). They let motion hit like the sound: an element that rises over the strike's attack and settles over its decay, a stutter that accelerates with the ratchet, the world arriving on the landing. They are a score to choreograph against, not data to chart.
REACH FOR THIS WHEN WHERE OR HOW AUDIO IS USED DEPENDS ON WHAT IS IN IT: placing a track so its drop lands on a reveal, choosing which section of a song fits a scene, checking whether an upload is speech or music before using it, or checking music you generated (does it have a clear beat, the energy arc you asked for, vocals where there should be none). A project's own music is already analysed — get_authoring_context shows it; use this for anything else.
Source: media_asset_id (an uploaded audio/video) or track_id (a music track). Up to 20 minutes.
SPENDS CREDITS, priced by length (about 1 credit per minute); library tracks are free. The same source is never billed twice — a repeat returns the existing analysis (force: true re-runs and charges again). A failed analysis is refunded. A music track (an upload included) is answered from its OWN analysis, free — the same features set_music, get_authoring_context and the render use; a fresh upload answers not_ready until that analysis lands (about half a minute).
ASYNCHRONOUS: returns an analysis id at "queued" — poll get_audio_analysis (about 30 seconds per minute of audio).
| Input | Type | Description |
|---|---|---|
media_asset_id | string | An uploaded audio or video (from list_media or an upload). Exactly one of media_asset_id / track_id / url. |
track_id | string | A music track id (browse_music, generate_music, or the project's music). |
url | string | The mix.wav url of a rendered score laid on a project (score tool, place) — so check_sync can read its beats. Exactly one of media_asset_id / track_id / url. |
force | boolean | Run again even if this source was already analysed (charged again). |
production | boolean | Also write the PRODUCTION SHEET (result.production): how the track is made — grid, arrangement, the drum kit's sound and density, the bass's sound design, the mix. Sound programs on this music read it (film.production, fit()) and get a scorecard against it. On a ready analysis it is added without a second charge. |
depth | string | "map" also writes the MUSIC MAP (result.map: status, url, summary): tempo, key, parts and the event score of how the track is built, from GPU stems when the track has them, else a CPU split (summary.stems says which). On a ready analysis it is added without a second charge. |
audio_strip
See a sound (waveform + spectrogram)
A picture of a sound, since you cannot hear it: the waveform over a log-frequency spectrogram, a seconds ruler and your marks drawn across both (words, lines, beats, cues), plus numbers: lufs (loudness), peak_db, bands {low <250 Hz, mid, high >4 kHz} in dB, and silences [[from, to]] of 0.3 s or more. Use it to check a take before placing it (did the voice clip, trail off, leave a long gap?), a music bed (where the drop is, whether it is muddy), or a mix against its cues. url = a file on our storage (a speak or compose_dialogue take, a generate_music track, an upload; "$voice.url" in a chain) or asset_id. from/to cut a window (at most 600 s). Free; seconds; the PNG is named by content, so asking again is free.
| Input | Type | Description |
|---|---|---|
url | string | The sound: a file on our storage (e.g. "$voice.url" in a chain). |
asset_id | string | Or a library audio (or video) asset id. |
from | number | Window start, seconds (default 0). |
to | number | Window end, seconds (default the end; at most 600 s after from). |
width | number | Picture width, 400–2400 px (default 1600). |
marks | array | Up to 120 marks on the sound's own clock: {t, label?, kind?: word|line|beat|cue}. |
audition_voices
Hear voices speak a line
Synthesise the SAME short line with up to 8 voices and get a playable audioUrl for each — the way to compare a shortlist, hear a voice that has no preview yet (new library voices, cheap Replicate models like Chatterbox / Kokoro), or hear a clone speak. Each voice is {voice_id} (a tts_preset id from list_voices) or {clone_id} (from clone_voice). text ≤400 characters; omit it for a natural sample in language (or in each voice's own language). The clip is the voice exactly as narration would render it. Free, rate limited per account. A voice that fails comes back with an error beside the others. To hear an EXCHANGE instead, pass dialogue {lines:[{speaker,text}], cast:{speaker:{voice_id}}} (no voices): each line is spoken by its speaker and the lines come back joined as ONE audioUrl, with each line's start/end on it.
| Input | Type | Description |
|---|---|---|
voices | array | Up to 8 voices; each has exactly one of voice_id / clone_id. |
text | string | The line to speak (≤400 characters). Omit for a default sample. |
language | string | Language to speak, e.g. 'fr' or 'en-gb'. Omit to use each voice's own. |
ask | string | The question shown with the clips. |
dialogue | object |
backfill_voice_analysis
Measure the silences in a project's existing voice-over
Voice-overs made recently carry a silence map (where each word boundary really is quiet), so cut_words and set_pauses land between words instead of biting them. Voice-overs made before that have none. This measures them from the stored audio and fills it in: no vendor call, no credits, nothing re-voiced, safe to run again. Returns per chapter how many blocks were analysed, skipped (already done or too short) or failed. Run it once on an older project before cutting words or placing pauses in it. Free.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
browse_music
Browse and choose a bed
The beds this account has GENERATED, side by side and playable — and selectable: the user picks one and can vary it or put it on a project without you relaying ids back and forth.
Use it whenever there is a choice to make. After generating two or three takes, call this rather than describing them: music is not judgeable from a description, and the point of making several is hearing them next to each other.
Different from list_catalog("music"), which lists the whole licensed library to pick from. This answers "what have I made?" and carries the grid (bpm, bars, key), the lineage (which takes are variations of which, and how far they travelled) and which bed is on the project — the things you actually compare takes by.
Pass project_id to mark the bed already in use and to offer attaching straight from the list. It does NOT filter to that project: a bed is reusable, and hiding the rest would hide most of the choice.
The selection is ORDERED. Picking one bed offers varying it or putting it on the project; picking SEVERAL, in the order they should play, offers making them the score — sections laid end to end with the last looping to the end of the video. That is how a palette becomes a piece: generate a motif, vary it a few ways, then choose the running order.
| Input | Type | Description |
|---|---|---|
query | string | Filter by name, case-insensitive substring. |
project_id | string | Mark which bed this project uses, and enable "use in this project". |
include_library | boolean | Also include licensed library tracks. Default false — generated beds only. |
include_pending | boolean | Also list your beds still generating or that FAILED (state + failure reason; a failed bed is refunded). Use it to check a commission that has not landed. |
limit | number | Max rows, default 40. |
track_ids | array | A shortlist to show instead of every bed, best first — when you are suggesting. |
prompt | string | The question shown above the picker, when you are asking the user to choose. |
clone_voice
Clone a voice from a recording
Make a narration voice from a recording of ONE person speaking — a library asset (media_id, from list_media) or a url to import (upload_url). The recording is checked first: at least 10s of speech, mostly speech (not music or silence), not too noisy or clipped. The best 10–30s becomes the reference and is transcribed. ASYNCHRONOUS: returns an id at status "processing"; poll get_voice_clone. When ready it gives a tts_preset_id for set_project_voice; when refused it gives the reason in words for the user ("found 2.1s of speech, need 10s").
CONSENT IS REQUIRED: pass consent.attested = true ONLY after the user has confirmed the voice is theirs or that the person recorded agreed to be cloned. Ask; never assume. Never clone a public figure. May be disabled on this deployment (feature_disabled) — say so plainly if it is.
| Input | Type | Description |
|---|---|---|
media_id | string | Library audio/video asset id (from list_media). Exactly one of media_id / upload_url. |
upload_url | string | An http(s) url of a recording to import, then clone. |
name required | string | What the voice is called in pickers, e.g. "Anna (my voice)". |
language | string | Language of the recording, e.g. 'en' or 'fr-fr'. Omit to detect it from the audio. |
model | "fish" | "chatterbox" | What the clone is made on: 'fish' (default — Fish S2.1 Pro, premium) or 'chatterbox' (Resemble Chatterbox on Replicate — standard tier, much cheaper; English via Turbo, other languages via Multilingual). |
consent required | object |
cut_words
Cut words out of the narration without re-voicing it
Text-based editing of a VOICED script block: strike a phrase (or a character range) out of its voice-over. Nothing is regenerated — the words stay in the script, struck through, and their audio is skipped in the editor preview, the export and the captions. A range that clips a word takes the whole word. Cut edges are placed in the silence between words, so no neighbouring word is bitten. action:"restore" puts struck words back (no range = all). Cuts belong to the current voice take: regenerating the block's voice drops them. The block must already have its voice-over (get_storyboard shows block ids). Free.
| Input | Type | Description |
|---|---|---|
chapter_id required | string | Chapter id. |
block_id required | string | Script block id. |
action | string | "cut" (default) or "restore". |
phrase | string | Exact phrase to act on — first occurrence, case-insensitive. |
char_from | number | Start of a character range in the block text (instead of phrase). |
char_to | number | End of the character range (exclusive). |
find_sfx
Find a sound effect by meaning and length
Find a sound effect for a sound you can describe: words plus how long it should last. Searches the house library first (cueable now: usable_in_render true, src on our storage), then Openverse (commercial licences only, CC0 and CC-BY). Each hit carries credit (CC-BY needs it on screen or in the credits), license, duration_ms, tags. Openverse hits are NOT cueable until cached (usable_in_render false). want_sec ranks the closest length first; position and behaviour are passed through for the fit step. Free.
| Input | Type | Description |
|---|---|---|
q required | string | What the sound is, e.g. "riser" or "paper rustle". |
want_sec | number | How long the sound should last, in seconds. Ranks the closest length first. |
position | string | Where it sits, e.g. "scene start" or "on the cut". Passed through. |
behaviour | string | How it moves, e.g. "rising" or "decaying". Passed through. |
limit | number | Max hits, 1–50. Default 12. |
fork_caption_formula
Fork a caption formula
Take a caption style that already EXISTS, change part of it, keep the result as your own. Free. The companion to save_caption_formula: that one writes a rule from nothing, which means knowing the whole vocabulary before you can start; this one starts from something that already works, which is how anyone actually authors.
from is any style id you can read — list_formulas(slot:"caption") shows each one's full spec, so read it, decide what to change, and send only that.
patch is DEEP-merged over the source: {"paint":{"color":"#F0E2C0"}} recolours the line and leaves the source's face and tracking alone. An explicit null REMOVES a key — {"field":null} is how you say "same look, no ring at all", which a merge could not otherwise express.
Mode, position and size are inherited from the source unless you override them: a stacked column that silently reverted to a centred bottom band would not be the thing you forked. The result is validated exactly as authoring from scratch is — composed and linted the way the renderer will — which matters MORE here, because a patch that misnames an ingredient would otherwise inherit a perfectly valid source and save quietly, ignoring the one thing you changed.
Only formula-authored styles can be forked. A catalog look, a v2 preset, or a hand-written template carrying its own markup will say so rather than accept a patch that would do nothing.
| Input | Type | Description |
|---|---|---|
from required | string | The style id to start from — anything published, or one of your own. |
name | string | Name for the fork. Defaults to "<source> (copy)". |
patch | object | Deep-merged over the source formula: recipe, succession[], accent, entry, life, exit, field, slot, paint, fonts[]. null at a key removes it. |
mode | string | Override the source mode: karaoke | chunk | hero. |
default_position | string | Override the source position: top | middle | bottom. |
default_font_size | number | Override the source default size. |
generate_music
Write a piece of music
Commission an instrumental bed that belongs to NO project yet — it lands in the music library and you attach it later. SPENDS CREDITS. Asynchronous: the bed lands a few MINUTES later, so do not wait on the call — poll browse_music with include_pending (a failed bed shows its reason and is refunded).
Use this to AUDITION. Choosing music means hearing a couple of options and picking one, and set_music binds a bed to a project the moment it is made — so generate two or three here, play them, then attach the one the user likes with set_music (existing project) or music_track_id on create_project (new one).
To iterate rather than re-roll, pass from_track_id: a VARIATION of a bed the user already liked — same room and instrumentation, a different take. That is what "more like that one, but calmer" means. A variation inherits the source's length, so duration_sec is ignored.
MODELS: model picks the maker (list_catalog("models"), modality music; each lists its price and limits). Omit it for ACE-Step, 2 credits, 30-300s. A listed model is priced on its own: per second of length, or flat per call. None of them follows a stated note sequence; mood, tempo and key are what steer them (for exact notes write a score and render it). loop_bars asks for a loop of exactly that many bars (a hold scene's unit); finish decides the ending: loop = cut to whole bars with the tail folded over the head so repeating it has no dip, piece = trim a silent tail only, none = as the model returned it.
Describe INSTRUMENTATION, mood and production in steer — never a tempo or key, which are separate fields; naming them in prose makes the model fight its own conditioning. cue sets density: hush is nearly still, drive is insistent. Beds are snapped to a whole number of bars so they loop cleanly under a video.
| Input | Type | Description |
|---|---|---|
steer | string | Instrumentation, mood, production — e.g. "warm rhodes, brushed kit, late-night room". NOT tempo or key. |
bpm | number | Tempo, 40-220. Defaults to the project tempo (120 when the project has none). Tempo and length constrain each other; an impossible pair is rejected before credits are spent. |
meter | number | Beats per bar. Defaults to 4. |
key_scale | string | e.g. "A minor". Optional. |
duration_sec | number | Desired length. ACE-Step: 30-300, snapped UP to the nearest whole bar the provider can hit exactly (its 0.04s grid) — at 100 BPM 4/4, 43.2 stays 43.2 (18 bars). A listed model has its own window (shorter is possible). Omit it and a loop takes the shortest legal bar-exact phrase (ACE-Step has a 30s floor, so a "4-bar" loop at 100 BPM comes back at 16 bars). Ignored when from_track_id is set. |
model | string | The music model, from list_catalog("models") (modality music). Omit for ACE-Step. |
loop_bars | number | A loop of exactly this many bars, e.g. 4. ACE-Step cannot go under 30s; a listed model can. |
finish | "loop" | "piece" | "none" | How the audio is finished: loop (whole bars, seam joins itself), piece (trim a silent tail), none. Default: the model's own habit; ACE-Step none. |
cue | "hush" | "develop" | "drive" | "lift" | "resolve" | Density and articulation. |
presence | "bed" | "present" | "feature" | How far forward the music sits. bed = stays under speech (default for hush/develop/resolve); present = a motif is allowed, still carved for speech (default for drive/lift); feature = no retreat at all — ONLY where no narration plays over it, such as an opening, a culmination or a tail. feature under continuous speech sounds worse than anything else here. |
accents | string | Specific instrumentation moments — "a single struck bell on the downbeat", "low brass swell into the last bar". The granular control a mood word cannot express. |
seed | number | Reproducibility — the same inputs and seed give the same bed. |
name | string | Name for the track in the library. Defaults to one derived from the steer. |
from_track_id | string | Vary THIS bed instead of generating from nothing — id of a track that already has audio. |
strength | number | With from_track_id: how far to travel from it, 0..1. Higher = less like the original. Default is the provider's. |
get_audio_analysis
Read an audio analysis
The state of an analysis started with analyze_audio: "queued"/"processing" (poll again in ~15 seconds), "ready" (result: detect, tempo, key, sections[], salient[], counts per event kind, condensed), or "failed" (error says why; the charge was refunded). Pass events to get the event lists themselves, windowed with t0/t1 in seconds: beat.tracked, downbeat, hit.kick, hit.snare, hit.hat, note.bass, note.lead, section, salient, key, music, speech, vocals, word, tag, and the sound captions: strike (t, db, lv 0..1, a attack s, dc decay s, hz, gl, br, nz, src), artic.ratchet / artic.roll / artic.flam (n, rate, accel ms per stroke — negative is speeding up, ioi, ramp), phrase (bar, len in bars, why). Free.
| Input | Type | Description |
|---|---|---|
analysis_id required | string | The id analyze_audio returned. |
events | array | Event kinds to include, e.g. ["downbeat","hit.snare"]. Up to 2000 per kind. |
t0 | number | Window start in seconds (default: the start). |
t1 | number | Window end in seconds (default: the end). |
get_captions
Get caption settings
Read the project's global caption default — on/off, style id, colour scheme, alignment, size, AND the stored fine-tune (style) as authored. Read before you write: set_captions DEEP-merges, so patching blind either duplicates a field or keeps one you meant to drop. A scene can override all of it — read that with get_scene (caption_style_id / caption_style).
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
get_voice_clone
Check a voice clone
The state of a clone started with clone_voice: "processing" (poll again in a few seconds), "ready" (tts_preset_id — apply it with set_project_voice; reference_url is their own recording, not a synthesis; audition_url is the CLONE speaking a sample line — play that), or "rejected" (reject_reason — tell the user what to record instead). Free.
| Input | Type | Description |
|---|---|---|
clone_id required | string | The id clone_voice returned. |
get_voice_performance
Check a take-to-voice run
The state of a run started with perform_voice: queued / listening / writing / speaking (poll again in a few seconds), "ready" (url of the spoken line, asset_id, text_with_tags — edit it and call speak to redo the line; heard = what the listener reported per stretch of the take) or "failed" (error; the credits were refunded). Calling it after the tags are written also speaks the line. Free to call.
| Input | Type | Description |
|---|---|---|
performance_id required | string | The id perform_voice returned. |
groove_library
Browse, read, save and delete loops in the loop library
The LOOP LIBRARY: every groove this account can drop into a project — curated loops (built in, read-only) and the account's own saved loops. action "list" browses (q searches id, name, note, tags and voices; patterns:true returns each pattern to read and learn from); "get" reads one with its pattern; "save" writes one of yours (a pattern, or from_project + pattern_id to keep a groove you made in a project); "delete" removes one of yours (projects that copied it keep their copy). To USE a loop, set_groove add_loops copies it into the project, then default_id / scenes pick it. Patterns carry no tempo or key — they play at the project's. Free.
| Input | Type | Description |
|---|---|---|
action | "list" | "get" | "save" | "delete" | Default "list". |
id | string | Loop id (get / save / delete): lowercase letters, digits, - or _. |
q | string | list: search words. |
source | "curated" | "mine" | list: only one half. |
patterns | boolean | list: include every pattern. |
name | string | save: display name. |
note | string | save: what it sounds like and when to use it. |
tags | array | save: tags, e.g. "half-time", "tense". |
pattern | object | save: the pattern JSON (the set_groove grammar). |
from_project | object |
list_voices
List the voices for a project
The voices available for this project's narration: the one currently set and the library (include_recent adds the ones this workspace uses). Short rows with caps (tags, direction, dialogue, clone); page with offset, drop heard voices with exclude. Each has a preview_url to HEAR, not an adjective. Free. Narrow with language / provider / gender / age_group / query; compact:false adds supported_languages and preview languages. To hear voices say the SAME line — or a voice with no preview — use audition_voices.
| Input | Type | Description |
|---|---|---|
project_id | string | Project id. Optional: without one you get the workspace-used and library buckets (no "currently set"), so you can pick a narrator before create_project. |
language | string | Locale prefix — 'en' matches en-US and en-GB — also matching multilingual voices that list it. |
provider | string | Only this TTS provider, e.g. 'replicate' (cheap Chatterbox / Kokoro / Qwen / Inworld on Replicate), 'inworld', 'fishaudio', 'qwen', 'gemini'. |
tier | string | 'standard' or 'premium'. |
clones | boolean | Only the account's own cloned voices. |
gender | string | Filter by gender. |
age_group | string | Filter by age group. |
query | string | Substring over name, description and category. |
limit | number | Library rows (default 24, max 60). |
offset | number | Rows to skip; use next_offset. |
exclude | string | Comma-separated voice ids to leave out. |
supports | string | Comma-separated, all required: tags, direction, dialogue, clone. |
include_recent | boolean | Add the workspace-used voices (default false). |
compact | boolean | Short rows (default true). |
perform_voice
Take to voice: your recorded performance, spoken in another voice
Turn a RECORDED TAKE into a line spoken by a chosen voice with the take's pacing, pauses, sighs and mood. The take is any audio asset (take_media_id, from list_media or create_upload; up to 60 s) — no voice clone is needed. Steps: a listening model describes the take (words and timings, sounds, mood), an LLM writes the same words with Drama 3 expression tags and "..." pauses, then the voice (voice_id from list_voices, or your clone_id) speaks it. ASYNCHRONOUS: returns an id; poll get_voice_performance (a cold start can take a couple of minutes; the poll that finds the tags written speaks the line). dry_run:true prices it without spending.
WHAT SURVIVES (measured): pacing, pauses, sighs and mood. NOT singing and NOT a real laugh: tags cannot sing; say so if the take is mostly singing. mode "convert" (keeping the performance itself) is not available yet. SPENDS CREDITS: listening per second of take, plus the voice at the speak rate; refunded if it fails. Confirm with the user before a real run.
| Input | Type | Description |
|---|---|---|
take_media_id required | string | Library audio/video asset id of the recorded take (list_media). |
voice_id | string | A voice from list_voices (stock Drama 3 voices carry the tags best). Exactly one of voice_id / clone_id. |
clone_id | string | One of your own voice clones (clone_voice). |
mode | "tags" | 'tags' (default): the take's delivery written as expression tags. |
line_hint | string | What you are after, in a few words, e.g. "sad ending". Passed to the tag writer. |
project_id | string | The project the line belongs to. |
dry_run | boolean | Quote only: credits for the listening and an estimate for the voice. Spends nothing. |
revoice
Re-voice the narration
Speak the narration again, optionally in a DIFFERENT voice: one call for a project, a chapter or one block. set_project_voice alone changes only the setting (what is already voiced keeps the old voice); this changes it AND re-synthesises every spoken block in scope, with word timings. Omit voice_id to re-take with the current voice (a re-roll of a block you did not like). SPENDS CREDITS per character: ALWAYS dry_run:true first. It prices the candidate voice without saving it and reports plan (blocks, characters) and warnings: pauses stay (they are text positions); cut_words strikes do not carry to the new take (re-apply them); scenes you authored are timed to the OLD words (check them against the new take). Returns voiceover_generations to poll with get_project_status. Compare voices first with audition_voices (free).
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
voice_id | string | tts_preset id to switch to (list_voices). Omit to re-take with the current voice. |
scope | "project" | "chapter" | "block" | How much to re-voice. Default "project". |
chapter_id | string | Required when scope is "chapter". |
block_id | string | Required when scope is "block". |
dry_run | boolean | True = price it and report the plan; changes nothing (the voice is NOT saved). Do this first. |
save_caption_formula
Author a caption formula
AUTHOR a caption look as a FORMULA and keep it as a reusable style you own. Free, and the companion to save_caption_style: that one CAPTURES the look a project is already wearing, this one WRITES a new rule from nothing.
A formula says how words MOVE, which the style layer cannot express at all: recipe one named look for the whole line — "velocity". succession a list; word k takes step k mod N. Two steps alternate, five cycle. Each step may carry its own paint, so every other word can switch face AND colour. accent a step for the EMPHASISED word alone, over whatever the line is doing — "velocity everywhere, bounce on the accent word". entry/life/exit pick the three axes yourself instead of naming a recipe. Each is an id or {spec, params}. slot WHERE the line sits: band (centred, y%), alternate (corner to corner — first half high left, its answer low right), stack (one word per line, a column), drift (each line a step lower). This is the only route to horizontal placement; the style position block cannot reach it. field a treatment wrapping each word. paint — colour, font, size, weight, italic, tracking — line-wide or per step. A #hex is literal; a bare name is a theme role.
Ingredient ids come from the library; a recipe carries its own signature colour and face, so naming one is usually enough. The formula is validated by COMPOSING and LINTING it the way the renderer will: an unknown ingredient or a broken template law is rejected with the reason, rather than quietly rendering as a plain caption. Always private; the id appears in list_catalog("caption-styles") with user_authored: true and is what you pass as set_captions style_id.
| Input | Type | Description |
|---|---|---|
name required | string | What to call it — how you will recognise it in a picker later. |
formula required | object | The formula: any of recipe, succession[], accent, entry, life, exit, field, slot, paint, fonts[]. id and name are set for you. |
mode | string | karaoke (per-word timings), chunk (whole line) or hero. Default karaoke. |
default_position | string | top | middle | bottom — where a project starts it. Default bottom. |
default_font_size | number | Default caption size. Default 60. |
save_caption_style
Save the caption look as a reusable style
Name the project's CURRENT caption look and keep it as a reusable style you own. Free. This is what turns authoring into a preset: set_captions writes a look onto one project, this names it, and from then on any surface reaches it by id — another project, the editor picker, or a later agent asked to make something "like that one". What is saved is self-contained (the base look merged with the fine-tune), so it reproduces without the project it came from. Always private; it shows up in list_catalog("caption-styles") with user_authored: true, and its id is what you pass back as set_captions style_id.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project whose current caption look should be saved. |
name required | string | What to call it — how you will recognise it in a picker later. |
search_sfx
Find a sound effect in the library
Search the SOUND EFFECT LIBRARY by words (every word must match the slug, name, category or tags) and/or category. Each hit carries src (on our storage), duration_ms, tags and default_volume. Cue one from a composition with data-tc-sfx-src="<src>" on the element whose motion makes the sound, or {"t": …, "src": "<src>"} in the x-tc-sfx tag; a sound you made yourself goes up with create_upload(kind:"sfx"). Free.
| Input | Type | Description |
|---|---|---|
q | string | Words to match, e.g. "door slam" or "whoosh". |
category | string | One category, exact. |
limit | number | Max hits, 1–100. Default 30. |
set_audio_fx
Put an audio effect on words of the narration
Apply an audio effect to specific words of a VOICED script block — "reverb on that word", a pitch drop on a phrase, a telephone band (highpass+lowpass), a level change, a pan. Nothing is re-voiced and nothing moves in time: captions and scene timing stay put, and reverb/echo tails ring over what follows. Address the words with words (exact text, first occurrence, case-insensitive; a partial word takes the whole word) or a char range. Effects and params (missing = default, out of range = clamped):
reverb {mix 0..1 (0.35), decayMs 100..6000 (1500), preDelayMs 0..200 (12), damping Hz (6000)}; echo {delayMs 20..2000 (260), feedback 0..0.9 (0.4), mix 0..1 (0.5)}; pitch {semitones -12..12}; lowpass {frequency Hz (1200), q}; highpass {frequency Hz (400), q}; gain {db -40..12}; pan {pan -1..1}. tail_ms overrides how long reverb/echo ring. Setting the same effect again on the same words replaces it. clear:true removes effects on the words (all words when none given; only kind fx when given). Effects belong to the current voice take — regenerating the block's voice drops them. Heard in the editor preview and the export (re-render or re-share to hear it). The same effects (plus eq, width, gain_db) go on a whole sound effect (data-tc-sfx-fx in the scene HTML) or a music/sfx clip (apply_changes edit_clip/add_clip fx), baked on the server. Free.
| Input | Type | Description |
|---|---|---|
chapter_id required | string | Chapter id. |
block_id required | string | Script block id. |
words | string | Exact words to affect — first occurrence, case-insensitive. |
char_from | number | Start of a character range in the block text (instead of words). |
char_to | number | End of the character range (exclusive). |
fx | "reverb" | "echo" | "pitch" | "lowpass" | "highpass" | "gain" | "pan" | The effect. Required unless clear:true. |
params | object | Numeric effect params, e.g. {"mix":0.5,"decayMs":2500}. |
tail_ms | number | Reverb/echo only: how long the tail rings past the words (ms). |
clear | boolean | Remove effects instead of adding one. |
set_captions
Set caption settings
Turn captions on or off, pick their look, and AUTHOR the look itself. Free, and unlike a theme change this needs no regeneration — captions are a render overlay, so the change lands on the next render. Style ids come from list_catalog("caption-styles").
style is where authoring happens: a CaptionStyle partial, DEEP-merged over what is stored (pass null to clear the whole fine-tune). Every geometric value is PROPORTIONAL, so one style renders correctly at 16:9, 9:16, 1:1 and 4:5. The blocks:
position { anchorX left|center|right, anchorY top|middle|bottom, offsetXPct, offsetYPct, safeAreaPct } — offsets are % of the frame axis.
layout { widthPct, textAlign start|center|end, maxLines, maxWords }.
font { family, weight, italic, transform, sizePct, letterSpacingEm, lineHeight, color } — sizePct is % of frame WIDTH and acts as a MAX the renderer fits down from. About 3 reads well at 1920×1080.
THREE TEXT ROLES, each the same vocabulary (color, background, fontFamily, weight, italic, transform, sizeEm, scale, letterSpacingEm, outline, shadow, radiusPct, padXPct), inheriting field-by-field from font: font is every word, activeWord is the word being spoken, keywordEmphasis is the words picked out as the point. Only the fields you set are stored, and an explicitly set field OUTRANKS the look own paint — so a role is how you make a stock look yours.
shadow / outline / glow / background — optional decoration; present means on.
Colours and families may be a theme token instead of a literal: { token: "color-role.accent" } or { token: "text-role.display" }, which re-colours with the theme. A literal is an explicit opt-out of theming.
Per-SCENE authoring is the same object on update_scene (caption_style).
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
enabled | boolean | Captions on or off. |
style_id | string | Caption style id from list_catalog("caption-styles"). |
color_scheme | "default" | "theme" | 'theme' paints the caption from the project theme's palette; 'default' keeps the style's own colours. |
alignment | string | e.g. "bottom", "center". |
font_size | number | Caption font size. |
style | object | A CaptionStyle partial, deep-merged over the stored fine-tune. See the description for the blocks and the three text roles. Read get_captions first. |
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_conductor
Read or edit the conductor: tempo map with ramps, signatures, markers, arrangement
The project's musical grid as a CONDUCTOR (a Logic-style tempo map): tempo points with curved ramps, signature changes, key changes, markers and an arrangement lane, anchored in MUSICAL position (q = quarter-notes from bar 1, or bar + beat) so an edit at bar 5 does not slide bar 9. Only project_id = READ (conductor + edit_version). With ops: id-addressed edits applied in order, all or nothing. dry_run:true writes nothing and returns the diff in seconds (bars that move, music-span scenes, sound cues). Sound cues and music loops follow a grid change like set_tempo; scene windows are NOT re-fitted (reported only). Pass expected_version. Free. Ops and shapes: guide "The conductor".
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
ops | array | Each {op, ...}: add_point{q|bar,beat, bpm?, curve?(-1..1), hold?} move_point{id, q|bar,beat?, bpm?} remove_point{id} set_curve{id, curve?|null, hold?} set_base_tempo{bpm} set_meter{beats,unit} set_origin{origin_sec} set_key{key_scale, q|bar?, id?} remove_key{id} add_signature{bar>=2, beats, unit} edit_signature{id,...} remove_signature{id} add_marker{q|bar,beat, label, color?} edit_marker{id,...} remove_marker{id} add_arrangement{start_bar,end_bar,label,color?,hold?{on,exit_bar?,exits?:[{on,bar}],quantize?:beat|bar,interactive?},then_bar?} edit_arrangement{id,...,hold?|null,then_bar?|null} remove_arrangement{id} set_warp{mode:off|on} replace_all{conductor}. ≤64. |
dry_run | boolean | Validate and return the diff; write nothing. |
ripple | "ripple" | "hold" | 'ripple': music-span scenes (timing music) keep their bars, so their scene windows are rewritten to the new grid in the same call (scenes it cannot move safely are left untouched and listed with a reason in diff.reflow); 'hold' (default): every window stays on seconds. |
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_cues
Name moments on a scene that other things can fire on
Put named moments on a scene — {name:"subscribe", at:"word:subscribe"} — so something else can be told to fire on them. A cue does NOTHING on its own; it is an address. Today the brand mark reads them: set_branding formula:"ghost" rests the mark low and lifts it on hit:<name>, which is how you get a subscribe bump on the exact word rather than on a beat. at is seconds from the scene start, or a moment on the scene's own rulers: "word:subscribe", "word:#4", "bar:3", "beat:7", "cam:hero", "end-2", with +0.25 / +1b / +2B offsets. PREFER the moment form — a cue pinned to a word survives a re-cut of the narration and one pinned to 4.2s does not. It resolves at render against the scene's measured grid; a cue that does not resolve is dropped rather than firing at zero. The list REPLACES what the scene carries (send [] to clear). Names are lowercase letters, digits and dashes, because they are addressed as literal text. Free; re-render to see the reaction in the MP4.
| Input | Type | Description |
|---|---|---|
scene_id required | string | Scene id (from get_storyboard). |
cues required | array | The complete cue list for this scene. Send [] to clear. |
set_groove
Read or set the project's grooves
The project's GROOVES: a synthesized loop per scene that plays on the project's tempo (bar n is bar n in every scene, so neighbouring grooves stay in phase across a join and a seam sound from set_transition lands on it). Set a default every scene plays, override ONE scene (pattern_id null = silent there, "inherit" = back to the default), let a scene play its fill into the next seam, add or edit library patterns, set gain_db and enabled. Presets: glass-acid, grain-garage, pluck-bells, stutter-house — copied into the library the first time they are used. A pattern is {bars 1–32 (≤16 tracks), tracks: [{voice, steps, notes?, fill?, octave?, gain?, pan?, tone?, duck?, crush?, vel?, nudge?, split?, dyn?}], swing?, res?, seed?, humanize?, room?}; voices kick clap hat ohat rim acid fmbass reese stab pluck grain glitch texture bell, and an ACOUSTIC KIT bd snare brush ride crash tomhi tommid tomlo pedal (measured drums: velocity changes the timbre, not just the level; and an ACOUSTIC BAND upright piano vibes (measured notes; a note sounds for the steps it is held with - ties); and a STRING SECTION strings trem spicc pizz (recorded notes: each note is played by the section whose register it is in — violins from G3, violas, celli, basses — so octave -1/-2 moves a part down the orchestra; a CHORD is voiced across the section — top note first violins, then seconds and violas, the lowest note to the celli (a low bass doubled by the basses), split:false to turn that off; each section sits in its own seat on the stage; vel is the level (pp→ff ≈21 dB) and crossfades soft to loud recordings; ties hold a strings/trem note, and a note starting as the last ends is played legato); a held brush "x--" is a sweep). steps are "." rest, x hit, X accent, g ghost, 2–4 ratchet (a roll), - tie (one string, or up to 32 bar strings); vel is 0..1 per hit, cycled — the part's dynamics; nudge (ms) lays a track back or pushes it; humanize {ms, vel} is a player's looseness; room 0..0.6 the space. Swing a jazz part by writing it on triplets (res 12: ride "x..x.xx..x.x"). notes are scale degrees in the project key, strings for accidentals ("b7", "#4"), an array is a chord, and a roman numeral is a chord in the key ("i", "iv", "V", "bVI", "ii°", "V7"). dyn is a part's dynamics as a curve — [[1,"pp"],[8,"ff"]] is an 8-bar crescendo (levels 0..1 or ppp…fff, straight lines between; vel then only shapes accents) — so write a swell as two points, not a velocity per note. Tempo and key always come from the project. Call with only project_id to READ; render:true also returns each scene's hits (trains, scene-local seconds) to sync visuals to, and its ears: loudness, peak, each voice's level against the loudest (balance, dB) and notes (a buried voice, a slice hitting the ceiling) — read them to hear what you wrote. A scene still rendering comes back {pending: true}: read again in a minute. Free.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
default_id | string | Groove every scene without an override plays (library or preset id). Empty string = none. |
scenes | array | Per-scene picks. |
upsert | array | Add or replace library patterns (validated). |
add_presets | array | Preset ids to copy into the library. |
add_loops | array | Loop-library ids (groove_library) to copy into this project. |
remove | array | Library ids to delete (the default and scene picks that use them are cleared). |
gain_db | number | Groove level in dB (−30..12, default 0). |
enabled | boolean | Grooves on/off for the whole project. |
render | boolean | Also render each scene's slice and return its hits (a read). |
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_mix
Read or set how the music sits under the voice
How the MUSIC sits under the VOICE — the same in the editor, the share link and the export. Two layers. MODE: "bed" ducks any music (tracks and grooves) under any voice, like a YouTube bed: it dips duck_db before the voice lands (attack_sec), stays down through breaths shorter than bridge_sec, comes back over release_sec, and carves a notch (carve_db at 2.5 kHz) so the words sit in it; "off" never ducks. New projects start on bed. MOMENTS are film-editor cues hung off a scene edge, so they move when the scene is re-timed, and they work whatever the mode: swell = the music takes the room (up db, default +5, over ramp_sec, held hold_sec, then back) — a title, a reveal, a pause in the voice; out = cue the music out (to silence over ramp_sec, default 1.5) until the next in — a serious line said dry; in = cue it back (only after an out, in the same chapter); hit = a stop-down, dead silence for hold_sec (default 0.5) then straight back — put it on a beat. edge "start" | "end" of the scene, offset_sec from it (negative = before: edge end, offset -4 = four seconds before the scene ends). Call with no changes to READ: mode, the effective duck, every moment with its index and at_sec on the film clock. SIDECHAIN FROM ANY SOURCE (ducks): up to 4 rules {source: "vo"|"sfx", target: "music"|"sfx", duck_db, attack_sec, release_sec, bridge_sec} — the target bus dips while the source sounds, the same ramp shape as the bed (default -8 dB on music, -4 dB on SFX). vo->sfx keeps sound effects out of the words; sfx->music dips the bed under a hit. The bed duck of mode "bed" stays as is; rules stack on top of it. Replaces all rules ([] clears). AUX SENDS (room): one shared reverb that buses feed — room {send: {vo, music, sfx} each 0..1 (default 0 = none), return_db -24..+6 (default -6), decay_ms 100..6000 (default 900), pre_delay_ms 0..200 (default 12), damping Hz 500..16000 (default 6000)}; null clears the room. Bus-level only, not per track. Both live in the project's mix settings, so they ride along when a project is forked or used as a basis. Free.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
mode | "bed" | "off" | "bed" = music ducks under voice; "off" = never. |
duck | object | |
ducks | array | Sidechain from any source — REPLACES all rules ([] clears), at most 4. The target bus dips while the source sounds. |
room | object | null | Aux sends: one shared reverb the buses feed (bus-level, not per track). A merge — null resets a knob; pass null for the whole room to remove it. |
add_moments | array | Moments to add. |
remove_moments | array | Indexes (from a read) of moments to delete. |
clear_scenes | array | Delete every moment on these scenes. |
moments | array | Replace ALL moments with this list ([] clears them). |
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_pauses
Set timing pauses in a script block
Set the pacing of a script block: a pause is SILENCE inserted into the voice-over timeline at a point in the text, so every word after it is heard later (the editor's pause chips). Nothing is re-voiced; the block's anchored scenes re-time (a scene window grows by the silence inside it) and the chapter timeline follows. Use it to recreate a source video's pacing, or to leave room for a scene to land. Place each pause with after_text (it goes right AFTER that phrase — case-insensitive; occurrence picks the n-th match, default 1) or a raw char_pos; duration_ms 1–20000. mode "replace" (default) makes the list the block's pauses — pauses:[] clears them; "merge" keeps existing pauses at other positions. Omit pauses to read the current ones. Returns each pause with its char_pos and the text just before it — check the placement. Pauses are text positions, so they survive a voice regeneration, but rewriting the block text clears them. get_storyboard shows block ids. Free.
| Input | Type | Description |
|---|---|---|
chapter_id required | string | Chapter id. |
block_id required | string | Script block id. |
pauses | array | The pauses (max 64). Omit to read the current ones. |
mode | "replace" | "merge" | "replace" (default): these become the block's pauses. "merge": keep existing pauses at other positions. |
set_project_voice
Set the project's narration voice
Set the voice this project's narration is spoken in. Free and reversible. It does NOT synthesise anything — voiceover is generated by resolve_project, which runs the voice BEFORE the scenes bound to it. A project with narration-bound scenes and no voice will have its resolve refused until one is set, because a scene authored with no word timings comes out at a default length and has to be paid for twice. Pick one from list_voices and say why it fits; changing the voice does not re-voice what is already generated.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
voice_id required | string | tts_preset id from list_voices. |
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_tempo
Read or set the project tempo
The project's musical grid: bpm, meter, key, where bar 1 falls (origin_sec) and tempo sections. Changing it does what the editor's tempo chip does: every grid-bound sound cue moves to the same bar:beat on the new grid, grid-bound motion follows at mount, and a music LOOP is re-conformed (time-stretched) to the new tempo — a full-length track keeps its own tempo, and going back to a loop's own tempo restores its original file. Send only what changes (e.g. just bpm); the rest is kept. A loop bake can take a few seconds and swaps itself in (status "pending"). FLAT edits only: it flattens tempo ramps into steps (the result says flattened_ramps) — use set_conductor for ramps, curves, signatures, markers. Call with only project_id to READ. Free.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
bpm | number | Tempo in quarter-note BPM, 20-400. |
meter | object | |
key_scale | string | null | Key, e.g. "C minor"; null clears it. |
origin_sec | number | Film seconds of bar 1, beat 1 (the first downbeat). |
sections | array | Tempo/meter/key changes from a point onward. Replaces the list; [] clears it. |
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. |
silence_trims
Read or set the silence trims of a script block
A silence trim tightens or stretches the NATURAL silence after a word (the quiet the voice itself left between two words), the way dragging the edge of a dark gap box on the timeline's voice track does. Omit at_audio_ms to list the block's trims. Pass at_audio_ms (the END of the word the silence follows, in the voice file: a word's endMs from get_audio_analysis / the cut_words reply) and delta_ms (> 0 stretches the silence, < 0 shortens it, 0 removes the trim; at most 5000 either way). Nothing is re-voiced; the block's anchored scenes re-time and the timeline follows. Authored pauses are separate (set_pauses); a trim belongs to the voice take it was made on. Free.
| Input | Type | Description |
|---|---|---|
chapter_id required | string | Chapter id. |
block_id required | string | Script block id. |
at_audio_ms | number | End of the word the silence follows, in the voice file (ms). Omit to list. |
delta_ms | number | Change to that silence in ms (> 0 longer, < 0 shorter, 0 removes the trim). |
suggest_voices
Suggest voices for a brief
Get a ranked shortlist of voices for a BRIEF — no line to speak needed. Say what the voice is for (brief: "calm documentary about space, older male") and/or narrow by language, gender, age_group, provider, tier; pass project_id to lean on this workspace's habits. Each voice comes back with a score, reasons (why it is on the list), and a playable sample_url. The list is spread across providers (at most 3 each) so it is a real choice, up to 8. audition:true also synthesises a default sample (or your text, ≤400) for shortlisted voices that have no sample yet. Free; only the audition half is rate limited. Then compare two or three on the actual line with audition_voices, and set_project_voice.
| Input | Type | Description |
|---|---|---|
brief | string | What the voice is for, in words (tone, subject, audience). ≤600 characters. |
language | string | Language to speak, e.g. 'fr' or 'en-gb'. Includes multilingual voices that list it. |
gender | string | Filter by gender. |
age_group | string | Filter by age group. |
provider | string | Only this TTS provider, e.g. 'replicate', 'inworld', 'fishaudio'. |
tier | string | 'standard' or 'premium'. |
project_id | string | Project id: leans toward voices this workspace already uses; its current voice is left out. |
limit | number | Voices to return (default 8, max 8). |
audition | boolean | Synthesise a sample for shortlisted voices that have none. Default false. |
text | string | The line for that audition (≤400). Omit for a default sample in the language. |
tighten_silences
Cut the dead air out of a project's narration
Tighten the pacing of a whole project (or one chapter) in one call. Finds every silence in the voice-over from the audio itself, cuts each one longer than keep_ms (default 300) down to keep_ms, and leaves the rest. The cuts are real: every word timing, caption and scene window anchored to the narration moves up with them, so anything tied to words follows with no further edit, and the film gets shorter. Not a stretch: the voice is untouched, only the quiet is removed. Keep a dramatic pause with keep:[{before:"Leave"}] (leave the silence just before those words) or {after:"..."} (just after), optionally scoped to a block_id, or keep_at_least_ms to leave every silence at least that long. dry_run:true reports what would be cut (per block: silences, savings, what was kept) and writes nothing; run it first. Re-running replaces this tool's own cuts and leaves cut_words strikes alone; silence trims on a tightened block are cleared. Older takes are measured first (free). Cuts belong to the voice take: re-voicing a block drops them, run it again. Anchor visuals to words, not absolute seconds, and re-timing stays free. Free.
| Input | Type | Description |
|---|---|---|
project_id required | string | Project id. |
chapter_id | string | Only this chapter (default: every chapter). |
keep_ms | number | Silence left between sentences, ms (default 300). |
edge_keep_ms | number | Silence left at the start and end of each block's audio, ms (default half of keep_ms). |
keep_at_least_ms | number | Leave every silence at least this long untouched (a deliberate pause). |
keep | array | Silences to leave as they are: {before:"phrase"} the silence right before those words, {after:"phrase"} right after, both to require both; block_id optional. |
dry_run | boolean | Report only; write nothing. |
transcribe
Read the words out of a recording
Timed words from a library recording or video. SPENDS CREDITS (~1/min).
| Input | Type | Description |
|---|---|---|
media_asset_id required | string | The library asset to transcribe (from list_media). |
project_id | string | Which project the transcript should ground. |
transcribe_media
Read the words out of a recording
Transcribe an audio or video asset that is ALREADY in the library, and ground the project in what was said. Word-level timings come back with it. Fast — about a minute of wall clock per hour of audio.
SPENDS CREDITS, priced by the length of the recording (~1 credit per minute of audio). The charge belongs to the REQUEST: polling an in-flight transcription is free, and the same asset is never billed twice. Confirm before transcribing something long.
REACH FOR THIS THE MOMENT A RECORDING IS THE SUBJECT. "Use this talk", "clip the highlights", "make something from this interview", "what does he say at the end" — all of them need the words first, and nothing else in this surface has read the file. Without it you are working from a file name: a video is captioned from ONE poster frame, so list_media and list_observations can tell you a clip exists and how long it is, never what is in it.
ASYNCHRONOUS: it returns status: "pending" and the work continues in the background. Poll rather than calling again — a second call on the same asset reports the state, it does not queue a second pass.
NO SPEECH, NO TRANSCRIPT: a recording the audio analysis heard no speech or singing in (an instrumental) is refused before any charge — force: true transcribes it anyway — and get_sources reports such a source usable:false, "instrumental — no speech detected", with no segments: text speech-to-text makes up over music is never a transcript.
| Input | Type | Description |
|---|---|---|
media_asset_id required | string | The library asset to transcribe (from list_media). An id, never a url. |
project_id | string | Which project the transcript should ground. Defaults to the asset's own project; required when the asset belongs to none. |
role | "subject" | "supporting" | "style" | How the transcript is used: "subject" (what the video is about — the default), "supporting" (evidence alongside other material). |
force | boolean | Transcribe even when the audio analysis heard no speech or singing in it (charged). |
voice_takes
List or switch the voice takes of a script block
Every time a block is voiced the result is KEPT as a take; the active one is what plays. Omit take_id to list the block's takes (id, length, words, which is active). Pass take_id to make that take active: nothing is re-voiced or billed, the block's anchored scenes re-time to its length and the timeline follows. Pauses carry over (they are text positions); cuts and silence trims were made on a specific take, so the reply's stale_edits counts the ones that no longer apply. Use it to compare two readings (switch, then check_sync / share_project) or to go back after a re-voice. get_storyboard shows block ids. Free.
| Input | Type | Description |
|---|---|---|
chapter_id required | string | Chapter id. |
block_id required | string | Script block id. |
take_id | string | The take to make active. Omit to list the takes. |