Editor

Edit the artifacts one video is made of: its theme config, transcript, caption pages, b-roll segments, presentation timeline and AI scenes.

Everything the generation pipeline produced is editable through a set of endpoints nested under /videos/{id}. They are all arrays or objects on the video document, so each one is read and written whole rather than patched item by item.

ArtifactEndpointExists on
Theme config/videos/{id}/configEvery video
Transcript and caption pages/videos/{id}/captionsVideos generated with transcribe
B-roll segments/videos/{id}/brollsVideos generated with addBRolls
Presentation timeline/videos/{id}/presentation-scenessubType: presentation only
AI scenes/videos/{id}/scenestype: ai only

Reading an artifact on the wrong pipeline is a 409 wrong-video-type. The same arrays can be read alongside the video itself with GET /videos/{id}?include=captions,bRolls,..., which is cheaper than four calls when you only need to look.

Read, modify, write

PUT replaces the whole array. An item you leave out of the body is deleted, so never construct an array from scratch: GET the current one, change what you need, and PUT the full result back.

The write path is deliberately lossy. Only the fields listed below travel on the wire, so a caller can never point a segment at an arbitrary asset or rewrite the editor's styling. Everything else is carried over from the stored item with the same id: an incoming item is layered on top of the one already saved, and an item whose id is unknown (or absent) is treated as new. Ids are minted when absent.

That merge is what makes a read-modify-write safe and a hand-built array dangerous. Post an array of fresh objects with no ids and you will keep the timings you sent and lose every resolved file, every animation and every sound effect the user chose.

Theme config

The config holds the caption styling and the presentation brand palette for one video. An empty object means the video has never been styled and renders with the editor defaults.

curl -X PATCH https://app.shortfast.com/api/v1/videos/7bQxL2mF9dR4tK1sv/config \
  -H "Authorization: Bearer sf_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "captionsPrimaryColor": "#FFD400",
    "captionsFontSize": 84,
    "captionsVerticalPosition": 62
  }'

The config is the one artifact that merges rather than replaces: PATCH shallow-merges the keys the body carries into the stored config and returns the whole merged object, leaving omitted keys alone. Send the config object itself or nest it under config. Unknown keys are dropped; a known key with a value outside its enum or range is a 400; a body carrying no known config key at all is refused rather than saved as a no-op.

DELETE /videos/{id}/config replaces the stored config with the editor defaults and returns them.

Transcript and caption pages

captions is the word-level transcript the pipeline produced. captionsPages is how those words are grouped into the chunks shown on screen. Both are empty until the video has been generated with transcription on.

PUT accepts captions, captionsPages or both. Whichever half you send is replaced wholesale; the other is left untouched.

curl -X PUT https://app.shortfast.com/api/v1/videos/7bQxL2mF9dR4tK1sv/captions \
  -H "Authorization: Bearer sf_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "captionsPages": [
      { "id": "p1a2b3", "startMs": 0, "endMs": 2000 },
      { "id": "p4d5e6", "startMs": 2000, "endMs": 3620, "hidden": true }
    ]
  }'

A caption word writes id, startMs, endMs, text, punctuated_word and confidence. A caption page writes id, startMs, endMs and hidden; its editor decorations (emoji, audioEffect) are read-only and preserved by id.

Caps per request: 6000 caption words, 2000 pages. endMs must be at or after startMs on every item.

B-roll segments

One segment per stretch of the video that shows footage or a generated image instead of the base video. Each segment carries both the editable brief (what it should show) and the resolved media the pipeline picked for it.

WritableRead-only, preserved by id
id, startMs, endMsurl, type, qualityUrls
text (the line of script it covers, 1000 characters)animation, transition, layout, scale
prompt (what an AI image should show, 1000 characters)hidden, audioEffect
keywords (search terms for stock footage, 200 characters)
mediaType: ai_image or stock_footage
curl -X PUT https://app.shortfast.com/api/v1/videos/7bQxL2mF9dR4tK1sv/brolls \
  -H "Authorization: Bearer sf_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "bRolls": [
      { "id": "b1a2b3", "startMs": 0, "endMs": 2400, "prompt": "A tired founder at a desk late at night", "mediaType": "ai_image" },
      { "startMs": 2400, "endMs": 5200, "keywords": "unfinished notebook", "mediaType": "stock_footage" }
    ]
  }'

The body can be a bare JSON array or wrapped as { "bRolls": [ ... ] }. Cap: 500 segments per request. The second segment above has no id, so it is created new and starts with no media attached.

Changing what a segment should show does not re-fetch its media on its own. Requeue generation with addBRolls: true (see Videos) to resolve new footage for the changed briefs.

Presentation timeline

The scene timeline of a presentation video: one entry per beat, each with its span in the video, the line of script it covers, and the visual template that renders it. Template ids come from GET /reference/presentation-templates and backdrop ids from GET /reference/presentation-backgrounds.

curl -X PUT https://app.shortfast.com/api/v1/videos/7bQxL2mF9dR4tK1sv/presentation-scenes \
  -H "Authorization: Bearer sf_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "presentationScenes": [
      { "id": "s1a2b3", "startMs": 0, "endMs": 3200, "text": "Ship your first video in under a minute", "template": "heroTitle", "tone": "light" },
      { "id": "s4d5e6", "startMs": 3200, "endMs": 7400, "text": "Three steps, no editing timeline", "template": "flowSteps", "tone": "accent" }
    ]
  }'

A scene writes id, startMs, endMs, text (300 characters), emphasis (the phrase inside text to stress, 60 characters), template, tone (light, dark, accent), caption (show or none), hidden, visual, content, params, direction and composed. The body can be a bare array or wrapped as { "presentationScenes": [ ... ] } ({ "scenes": [ ... ] } is accepted too).

Unlike caption pages and b-rolls, a presentation scene is rebuilt from what you send rather than layered over the stored one: a recognized key you omit is gone from the saved scene. Read the timeline, edit it, and put the whole thing back.

Timings are validated against the end of the transcript, or the source duration when there is none. A scene outside that window is clamped, one that ends at or before it starts is dropped, and scenes are stored sorted by startMs. A video with neither a transcript nor a known duration cannot place scene timings at all and is refused with 409 warning-source-not-ready.

POST /videos/{id}/presentation-scenes/generate hands the job back to the AI director: it re-cuts the presentation from the existing transcript, picking a template, a tone and a visual for each beat, and replaces the stored timeline with the result. It charges credits, and it is synchronous but slow, because it plans the whole film in one pass. The video must already have a transcript.

AI scenes

The scene list of an AI video: one entry per shot, with the prompts that drive its still and its motion, the narration spoken over it, and the media rendered so far. Unlike the other artifacts, scenes are edited one at a time.

CallWhat it does
POST /videos/{id}/scenes/generatePlans a whole scene list from the video's prompt. Charges credits and replaces whatever scenes exist
POST /videos/{id}/scenesInserts one empty scene after afterSceneId, or appends when no id is given. Free: nothing is generated yet
PATCH /videos/{id}/scenes/{sceneId}Edits imagePrompt, animationPrompt, narration, voiceDescription or duration. Provide at least one
DELETE /videos/{id}/scenes/{sceneId}Removes the scene
POST /videos/{id}/scenes/{sceneId}/imageGenerates a new still and attaches it. Charges one image generation
curl -X PATCH https://app.shortfast.com/api/v1/videos/7bQxL2mF9dR4tK1sv/scenes/n1a2b3 \
  -H "Authorization: Bearer sf_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "narration": "Most founders burn out from never finishing anything.",
    "duration": 7
  }'

duration is 3 to 20 seconds; the prompts and the narration are capped at 2000 characters each and voiceDescription at 300. A newly added scene starts as a 5 second DRAFT with empty prompts, and inherits the preceding scene's voiceDescription so one character stays consistent. A video holds at most 50 scenes, and a 51st is a 409 limit-reached.

Every editable field is content, so editing, adding, removing a scene or regenerating its still makes the output stale: videoUrl on the scene and renderUrl on the video are cleared. Regenerate the still with POST /videos/{id}/scenes/{sceneId}/image, then re-render before publishing.

On POST .../image, pass prompt to use a different description for this run; it is saved as the scene's imagePrompt. Without it the stored imagePrompt is used, and a scene with neither is a 400. UGC videos reuse one character image across every scene, so a per-scene regeneration does not apply there and is refused with 409 warning-invalid-input.

Reference

On this page