Skip to main content
This is the flow the API is named for. You have prose already written, and you want it narrated and rendered. It is one call, then polling, and the file arrives as a URL you download.
Send the text itself. You do not need to upload anything first, create a project first, or find an id for your lesson in some catalogue. story is the input.

The call

That is the whole request. story carries up to 40,000 characters — a long lecture fits comfortably. Everything else is optional:
story is rendered by the stories domain, and sending it alongside a different domain is refused. The other domains (course, science, algorithms, chess, howto, ugc) read their subject out of this deployment’s own catalogue by videoId, slug or topic — they render lessons that already exist on our side, not text you supply. If you are bringing your own writing, story alone is the right call and domain should be left out.

Collect the file

status: "queued" is acceptance, not completion. Rendering takes minutes. Poll:
While it works:
When it is done:
job.artifacts.video is your video. Fetch it directly — it points at the origin holding the bytes, not back through this API, which is why a multi-gigabyte render downloads at the origin’s speed rather than a gateway’s. Captions come with it, as both SubRip and WebVTT. Branch on status, never on phase: status is a closed set, phase is the pipeline’s own stage word and changes between releases. done and failed are the terminal ones. Polling every 15–30 seconds is plenty.

Cut shorts from it

Once the render is done, its job id is the input to the derivative pass:
Passing the same projectId files the render and its clips in one container you can read back with a single GET /v1/projects/{projectId}.

If the document is not written yet

video.render narrates prose you already have. When you have a subject rather than a manuscript, write it first and render the result:

Text capabilities

text.research for a brief, text.paraphrase to restyle something long, text.script when you are working from a seed video.

Jobs & polling

The full status table, and why awaiting_review never advances on its own.

Retrying without paying twice

The Idempotency-Key above is not decoration on a 300-credit call. A retry with the same key and the same body replays the stored response instead of starting a second render, so a client timeout cannot become two charges. The same key with a different body answers 409 conflict rather than a wrong result. A render that fails after being accepted is refunded against the key it was billed under. You are never charged for a video that does not exist.