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
Written by Sume