Videos
The core loop of the ShortFast API: pick a pipeline, write a brief, queue generation, poll it, render, and download.
A video is one document that carries the brief you sent, the assets the pipeline
produced, and two independent jobs: generation (process) and render
(render). Everything else in the API hangs off it.
Generation and rendering are asynchronous. No endpoint waits for a job: POST /videos returns as soon as the work is queued, and a generation takes minutes.
Poll GET /videos/{id}, or subscribe to
video.generation_completed and let the webhook tell you.
Pick a pipeline
type selects the pipeline and subType the variant. Four combinations can be
created from a JSON brief:
type | subType | What it makes |
|---|---|---|
faceless | generic | A written script, a voiceover, captions, and stock footage or AI images cut against the transcript. The default short-form video. |
faceless | presentation | A narrated presentation cut into scenes, each one rendered by a visual template on a branded backdrop. |
ai | generic | A sequence of AI shots: one still and one motion prompt per scene, with narration spoken over it. |
ai | ugc | The same AI pipeline, built around one recurring character image reused across every scene. |
A third type exists on read: video is an uploaded source file running the
editable pipeline. It cannot be created here, because a JSON brief cannot supply
the file. POST /videos/{id}/convert turns a finished ai video into one.
The brief
Send the brief flat, or nested under brief. The nested form wins, so a
serialized video can be posted straight back. Unknown keys are ignored; a known
key with a malformed value is a 400.
| Field | Values and limits |
|---|---|
title | 200 characters |
prompt | 500 characters. What the video is about |
inputMode | idea writes the script from prompt; script uses the text you supply |
text | The script. 2000 characters on short, 15000 on long, and at most 120 spoken words on a presentation |
scriptPrompt | 500 characters. Steering for the script writer |
customInstructions | 300 characters |
videoDuration | short or long. long (8 to 15 minutes) needs an Advanced plan |
aspectRatio | 9:16, 4:5, 1:1, 16:9 |
language | auto, or a code such as en or pt-BR (10 characters) |
imageStyle | stock_footage, realistic, mixed, custom |
customStyleText | 100 characters. Read when imageStyle is custom |
voiceId | An id from GET /reference/voices |
voiceProvider | openai or gemini. The gemini tier is the premium expressive one and costs more per minute |
mood | energetic, inspiring, suspenseful, thrilling, calm. The music bed |
showSubtitles | Whether captions render over the video |
description | 300 characters. The social caption a publish posts with the video |
descriptionPrompt | 500 characters. Steering for the caption writer |
config | The theme block, same shape the editor reads |
Presentation videos add presentationTone (an id from
GET /reference/presentation-tones) and presentationAssets: up to 8 entries of
{ type: "image" | "video", url, description } for the director to place on a
scene.
AI videos add targetDuration (5 to 60 seconds), and UGC videos add
characterImageUrl and characterDescription (2000 characters) for the
recurring character.
Build a valid brief against the reference lists rather than hardcoding:
/reference/voices, /reference/presentation-tones,
/reference/presentation-templates, /reference/presentation-backgrounds,
/reference/aspect-ratios and /reference/image-styles are static and
unpaginated.
Job flags
process carries the per-run job flags. They live on the job, never on the
brief, and an omitted flag keeps whatever the video already has:
| Flag | Effect |
|---|---|
transcribe | Transcribe the audio into the word-level caption track |
addBRolls | Cut the script into b-roll segments and resolve media for each |
render | Queue the render as soon as generation finishes, without a second call |
1. Create and queue generation
curl -X POST https://app.shortfast.com/api/v1/videos \
-H "Authorization: Bearer sf_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 4f1c9e2a-6b81-4f0d-9a3e-2c5d7e8b1a44" \
-d '{
"type": "faceless",
"subType": "generic",
"prompt": "Why founders burn out and what to do about it",
"inputMode": "idea",
"videoDuration": "short",
"aspectRatio": "9:16",
"language": "en",
"imageStyle": "mixed",
"voiceId": "Enceladus",
"voiceProvider": "gemini",
"mood": "inspiring",
"showSubtitles": true,
"process": { "transcribe": true, "addBRolls": true, "render": true }
}'The response is the whole video with process.status: "QUEUED". Generation
charges credits and counts against the monthly generation limit, so it can be
refused before the job is queued with warning-no-credits,
warning-plan-generations, warning-plan-storage or warning-plan-duration.
isAutomation is refused here: create a blueprint with POST /automations
instead (see Automations).
To requeue generation on a video that already exists, POST /videos/{id}/process with the flags alone. A video whose generation is already
QUEUED or STARTED is refused with warning-already-processing.
2. Poll until generation finishes
curl https://app.shortfast.com/api/v1/videos/7bQxL2mF9dR4tK1sv \
-H "Authorization: Bearer sf_live_xxxxxxxxxxxxxxxxxxxxxxxx"Both jobs report the same four statuses, plus a progress percentage:
| Status | Meaning |
|---|---|
QUEUED | Accepted and waiting for a worker |
STARTED | Running. process.statusText names the step in plain words |
COMPLETED | Finished |
FAILED | Stopped. error.code, error.blocked and error.action say why and what clears it |
Poll every few seconds. A FAILED job with error.blocked: true is an expected
limit rather than a crash, and error.action (upgrade, credits, connect,
configure, none) is the fix.
GET /videos collapses both jobs into one status you can filter on, in this
precedence: failed if either job failed, then rendering, then processing,
then ready (a render exists), otherwise draft. The list also accepts type,
subType and q (a case-insensitive title search).
3. Render, poll, download
Once process.status is COMPLETED, queue the render, poll it, and ask for a
download URL. That half of the loop is covered in
Rendering and download.
curl -X POST https://app.shortfast.com/api/v1/videos/7bQxL2mF9dR4tK1sv/render \
-H "Authorization: Bearer sf_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Idempotency-Key: 8c3f1b90-77aa-4e51-b0d2-1e9f4a6c2d33"Passing process.render: true at creation chains this automatically, so a
one-shot integration only ever polls for status: "ready".
Reading a video back
GET /videos/{id} omits the content arrays by default, because they dominate the
payload. Opt into the ones you need:
curl "https://app.shortfast.com/api/v1/videos/7bQxL2mF9dR4tK1sv?include=captions,captionsPages" \
-H "Authorization: Bearer sf_live_xxxxxxxxxxxxxxxxxxxxxxxx"Known keys are captions, captionsPages, bRolls, presentationScenes and
scenes. An unknown key is dropped rather than rejected, so a newer client still
gets its video. An include passed to the list endpoint is ignored: the heavy
arrays never ride on a list.
Editing an existing video
PATCH /videos/{id} accepts only the brief fields that still mean something once
a video exists: title, aspectRatio, showSubtitles, description,
descriptionPrompt, prompt, subType, targetDuration, characterImageUrl
and characterDescription.
Every other brief field is generation-time only and is refused with a 400
naming it, rather than silently dropped. Editing does not requeue anything: run
POST /videos/{id}/process or POST /videos/{id}/render afterwards if the
change should reach the output.
Rewriting the script or the caption
Two synchronous writers charge one generation's worth of credits each and store their result on the brief:
POST /videos/{id}/scriptwritesbrief.text. Passpromptto restate what the script should be about; it is saved asscriptPromptand reused next time.POST /videos/{id}/descriptionwritesbrief.description, the caption a publish posts alongside the video. Passpromptto steer it; it is saved asdescriptionPrompt.
Both return the finished text in the response. A writer that returns nothing
usable is a 502: retry in a moment.
Clips and variations
POST /videos/{id}/clips creates a new video from this one, pointing back at it
through parentVideoId:
curl -X POST https://app.shortfast.com/api/v1/videos/7bQxL2mF9dR4tK1sv/clips \
-H "Authorization: Bearer sf_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "regenerate": true, "render": true }'regenerate: true rewrites the script and visuals (charging credits) instead of
reusing them. render: true queues the render as soon as the clip is ready.
GET /videos/{id}/clips lists what a video has produced, sorted on creation time
so a running job cannot shuffle rows between two pages of the same walk.
Deleting
DELETE /videos/{id} removes the video, every clip made from it, and the stored
files they own, then credits the freed bytes back to the workspace storage quota.
It cannot be undone, and the response reports deletedClips and freedBytes.
Reference
Rendering and download
Queue the mp4, poll its progress, and get a URL that resolves.
Editor
Edit the transcript, the b-rolls, the presentation timeline and the AI scenes.
Publishing
Post a rendered video to your connected social accounts.
Credits and limits
What generation costs and which ceiling refuses it.
Webhooks
Stop polling: subscribe to the lifecycle events instead.