Validate video timeline JSON before rendering: Sume's plan preflight
POST /v1/timeline-1.0/plan checks a Timeline 1.0 document without billing and returns its duration, billable minutes, cost estimate, and filtergraph summary.

To validate a video timeline before rendering it with the Sume API, send the same JSON body to POST /v1/timeline-1.0/plan. The plan runs the schema, the Sume-host URL checks, and the compiler, then returns a timeline_plan with the duration, billable minutes, a cost estimate, and a filtergraph summary. It creates no job, reserves no credits, and downloads no media.
The facts below come from the Timeline 1.0 docs and the plan entry in the Sume API reference, read on 2026-09-26. Anything described as current behavior is read from Sume's code. The render itself is covered in How to assemble a long-form video.
How do I call the plan?
Send the render body unchanged: the plan takes the same request schema as POST /v1/timeline-1.0/render. It authenticates with your API key like any other route, and Idempotency-Key is not required. A valid document answers 200; one that breaks a rule answers 400 with a stable error code.
curl -X POST https://api.sume.com/v1/timeline-1.0/plan \
-H "Authorization: Bearer $SUME_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"audio": {
"url": "https://media.sume.com/artifacts/artf_demo/voice.wav",
"duration_seconds": 95
},
"video": [
{ "source_url": "https://media.sume.com/artifacts/artf_demo/intro.mp4", "start": 0, "duration": 40 },
{
"source_url": "https://media.sume.com/artifacts/artf_demo/talk.mp4",
"start": 40,
"duration": 55,
"transition": { "type": "fade", "duration": 0.25 }
}
]
}'What does the plan return?
Every field below is required in the response. For the 95-second spine above, billable_minutes is 2. Because output.fps is omitted, the current code compiles that plan at 30 fps, where the 0.25 s fade is 7.5 frames, so warnings[] carries transition_snapped_to_frame.
| Field | What it holds |
|---|---|
object | timeline_plan. |
valid | A boolean. In the current code a failing document gets a 400, not a plan. |
duration_seconds | The output length, audio.duration_seconds. |
segment_count | The number of compiled video[] slots. |
coverage_seconds | In the current code, where the last slot ends: its start plus duration. |
billable_minutes | In the current code, ceil(duration_seconds / 60), at least 1. |
estimated_cost_usd_micros | The cost estimate, in millionths of a US dollar. |
transition_count | Transitions compiled into the graph. |
filtergraph_summary | A string summarizing the compiled graph. |
warnings[] | Each with code and message, plus optional segment_index and context. |
What does the plan check?
It runs the schema and admit bounds, the Sume-host check on every URL, and the compiler: the program rules and refusal codes listed in How to assemble a long-form video, from timeline_must_start_at_zero to too_many_chained_transitions. In the current code the plan and the render call the same validators, so a 400 from the plan carries the code the render would return.
What can't the plan predict?
The plan never downloads media, so anything that depends on the files shows up only when you render:
- Short sources. A slot longer than its clip is padded or looped, and a boundary without enough footage for its transition becomes a hard cut. The docs say a plan cannot predict these warnings.
- A soundtrack shorter than the output (
soundtrack_shorter_than_spine): in the current code the plan does not know the bed's length. - Frame rate. With
output.fpsomitted, the current code compiles the plan at 30 fps, while the render uses the rate its sources run at, so transition snapping can differ. Setoutput.fpsto make them match. - Missing clips. In the current code the plan checks each URL's host but skips the request the render makes to each file, so a missing clip passes the plan and the render refuses it with
source_not_found.
Does the estimate match what the render reserves?
Not exactly. In the current code estimated_cost_usd_micros is billable_minutes at the list rate, $0.10 per output minute on API pricing, with no agent fee. The render reserves ceil(audio.duration_seconds / 60) minutes at that rate plus a 5.5% agent fee by default. The docs say to confirm the rate live in GET /v1/catalog.
The plan also does not check your balance: only the render can answer 402 for insufficient funds. For other ways to see a price before you spend, see how to estimate AI video cost.
How do I use the plan in a pipeline?
Treat it as an unbilled compile step between building the document and paying for it:
- Build the document and call the plan.
- On a
400, fix the field the error names and plan again. Nothing was billed. - Check
duration_seconds,coverage_seconds, andbillable_minutesagainst what you meant to render, and readwarnings[]. Snapped transitions and ignored still motion are soft warnings, not failures. - Render with an
Idempotency-Key, then read the result'swarnings[]for the file-dependent cases the plan cannot see.
Sources
Related posts
More in Media tools
- Video editing API limits: Sume media tool caps in one table
Source and output lengths, counts, and sizes for Sume's trim, filter, detach, frames, inspect, captions, and Timeline APIs, each linked to its docs page.
- How to add bilingual subtitles to a video
Burn two languages onto one video with Sume: put both lines in each cue of one caption job, or run two jobs, each language at its own height.
- Add captions to a long video by API: split, caption, rejoin
One Sume caption job takes up to 60 seconds of video. Cut a longer video into chunks, caption each one, then rejoin them over the original audio.
- How to add an image to a video at a specific time
Show a picture only between two timestamps: compose it over that stretch of the clip, or cut to it as a still, then rebuild the video in one render.
Written by Sume