Video transitions API: crossfade, wipe, and slide between clips
Add fade, wipe, slide, or dissolve transitions between clips in a Sume Timeline 1.0 render: up to 1 s each, snapped to whole frames, at most 8 in a row.

To add transitions between clips with the Sume API, set transition: { type, duration } on any video[] slot after the first in a Timeline 1.0 render (POST /v1/timeline-1.0/render). type is fade, wipeleft, wiperight, slideup, slidedown, or dissolve, and the transition describes the boundary into that slot.
The facts below come from the Timeline 1.0 docs and the field descriptions in the Sume API reference, read on 2026-09-26. Anything described as current behavior is read from Sume's code. The rest of the render document is covered in How to assemble a long-form video.
What happens at each boundary?
Each clip must already be your workspace's media.sume.com artifact or asset, such as the output of an earlier Sume job. A transition's duration is capped at 1 s by the request schema; the table covers what else can happen at a boundary.
| Boundary | What happens | Code |
|---|---|---|
No transition | A hard cut | None |
transition on video[0] | Refused | transition_on_first_segment |
type outside the six values | Refused in the current code | invalid_transition_type |
| Longer than 50% of the shorter neighbor | Refused | transition_too_long |
| Rounds to zero output frames | Refused | transition_not_frame_aligned |
| Off the output frame grid | Snapped to the nearest frame, with a warning in the current code | transition_snapped_to_frame |
| Incoming clip too short for the transition | Drawn as a hard cut, with a warning in the current code | transition_downgraded_to_cut |
| Ninth transition in a row | Refused | too_many_chained_transitions |
| Slot overlaps the previous one by more than the transition | Refused | segment_overlap |
How do I add a crossfade between two clips?
Declared starts are authoritative, so do not shift a slot to make room for the fade. In the current compiler the crossfade into a slot runs over the transition's length just before that slot's start (the xfade offset is start minus the transition's duration), and the incoming clip supplies its slot's duration plus the transition's length from its source_in.
curl -X POST https://api.sume.com/v1/timeline-1.0/render \
-H "Authorization: Bearer $SUME_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: timeline-transitions-001" \
-d '{
"audio": { "url": "https://media.sume.com/artifacts/artf_demo/voice.wav", "duration_seconds": 18 },
"output": { "fps": 30 },
"video": [
{ "source_url": "https://media.sume.com/artifacts/artf_demo/a.mp4", "start": 0, "duration": 6 },
{
"source_url": "https://media.sume.com/artifacts/artf_demo/b.mp4", "start": 6, "duration": 6,
"transition": { "type": "fade", "duration": 0.5 }
},
{
"source_url": "https://media.sume.com/artifacts/artf_demo/c.mp4", "start": 12, "duration": 6,
"transition": { "type": "wipeleft", "duration": 0.4 }
}
]
}'Why is my transition a different length than I asked for?
Transitions render as whole frames. In the current compiler a duration off the frame grid is snapped to the nearest whole frame, moving it at most half a frame, and the result warns transition_snapped_to_frame with the requested and applied seconds; the timeline position is unchanged. For example, 0.30 s at 25 fps is 7.5 frames and renders as 8 frames (0.32 s). A duration that rounds to zero frames is refused with transition_not_frame_aligned rather than silently becoming a hard cut.
The grid depends on the output rate. With output.fps omitted, the render picks the rate the sources run at, so the frame check at submit uses 24 fps, the slowest rate an omitted value can resolve to. When the exact length matters, set output.fps (24, 25, 30, or 60) and pick whole-frame durations, as the example above does at 30 fps.
Why did a transition become a hard cut?
In the current compiler, if the incoming clip does not hold its slot's duration plus the transition's length after source_in, the render draws that boundary as a hard cut and warns transition_downgraded_to_cut; the timeline position is unchanged and the job still completes. Lower source_in, shorten the slot, or use a longer clip to keep the transition.
Only the render can see this. The unbilled plan preflight does not download media, so it cannot predict short-source downgrade warnings.
How many transitions can I chain?
At most 8 in a row, of any type; the docs call them adjacent fades. A ninth in a row is refused with too_many_chained_transitions, and the fix is a hard cut: one slot without a transition. The code gives two reasons: chained crossfade offsets accumulate, and the cap guarantees every timeline has a hard cut where the chunked render can split. The default render.strategy, auto, chunks past 12 segments.
How do I fade in from black or out to black?
Use output.fade_in_seconds and output.fade_out_seconds, 0–5 s each, not a slot transition. The API reference says they fade the picture from and to black and the sound from and to silence, snapped to whole frames. Together they must fit inside the output, or the request is refused with edge_fades_exceed_output.
The render's public rate is $0.10 per output minute on API pricing, and the reserve is ceil(audio.duration_seconds / 60) minutes.
Sources
Related posts
Written by Sume