Media tools

How to use Sume's Timeline compose and Timeline audio APIs

Timeline compose puts one still and one video in the same frame as a new MP4. Timeline audio joins or splits Sume-hosted audio into reusable files.

6 min readSume
All posts

Timeline compose (POST /v1/timeline-1.0/compose) puts one Sume-hosted still and one Sume-hosted video on screen at the same time and returns one MP4 shot. Timeline audio (POST /v1/timeline-1.0/audio) joins or slices Sume-hosted audio into durable media.sume.com files. Both prepare material for a Timeline 1.0 render; neither sequences clips.

The facts below are from the Timeline compose and Timeline audio docs, read on 2026-09-25.

When do I need compose or audio instead of the render?

  • Compose builds one shot. Sequential image-then-video is not a compose mode: adjacent video[] slots on the render already do that.
  • Timeline audio mints a reusable file. A join needed only inside one render belongs on the render's audio.parts[]; skip the job.
  • Both take only URLs already on this workspace's media.sume.com. Off-host URLs are rejected at admit, so import first with POST /v1/media-imports (see media inputs and outputs).
  • Both require Idempotency-Key. The default mode is async; mode: "sync" waits up to 30 seconds for a 200 finished job, otherwise you get 202 and poll.

How do I put a still and a video in one frame?

Send operation (stack or overlay), image.url, and video.url. The stack-or-overlay field is operation, not mode, which stays the usual async / sync / webhook option. The image must probe as a still (compose_image_not_still) and the video as a video (compose_video_not_video).

Output length always comes from the video layer: video.duration, else the rest of the file from source_in. The still is held for the whole clip and can never lengthen it. The ceiling is 300 s, and a video.duration past the source clamps with the warning compose_duration_clamped_to_source.

Default output is a 1080×1920 MP4 at the video layer's own frame rate. Set output.width / output.height to the timeline you will assemble into, so the shot is not rescaled twice. Audio passes through from the video; a mute video only warns compose_video_has_no_audio, because the Timeline 1.0 spine supplies the audio at assemble time.

curl -X POST https://api.sume.com/v1/timeline-1.0/compose \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: timeline-compose-001" \
  -d '{
    "operation": "stack",
    "image": { "url": "https://media.sume.com/artifacts/artf_demo/banner.png" },
    "video": { "url": "https://media.sume.com/artifacts/artf_demo/talk.mp4" },
    "layout": { "split": "horizontal", "image_region": "top", "ratio": 0.5 },
    "output": { "width": 720, "height": 1280, "fps": 25 }
  }'

Which layout keys go with stack and overlay?

stack tiles two regions of one frame. Its defaults, horizontal / top / 0.5, give a half-banner: still on top, video underneath, with the video taking the exact remainder. Mixing stack keys with overlay keys is a 400 (compose_stack_takes_no_overlay_layout / compose_overlay_takes_no_stack_layout).

Layout keys from Timeline compose, read 2026-09-25.
`layout` key`stack``overlay`
splithorizontal or verticalIllegal
image_regiontop / bottom on a horizontal split; left / right on a vertical splitIllegal
ratioThe still's share of the frame, 0.1–0.9Illegal
image_fit / video_fitcover, contain, or stretch (blur is not a compose fit)video_fit only
positionIllegaltop, center, or bottom
width_ratioIllegal0.05–1 of width (default 0.9); the plate keeps its aspect
margin_ratioIllegal0–0.45 of height (default 0.05)

How do I join or split audio files?

operation: "concat" takes parts[]: 1–20 ordered parts, each { url, source_in?, duration? }. The join is sample-domain, with no re-TTS and no silence at the seams, and parts must share one channel layout. The result (kind: timeline_audio) has one audio_url, duration_seconds, and segments[] (index, start, duration_seconds): the offsets to re-base Timeline 1.0 video[].start against.

operation: "split" takes a top-level url and ranges[]: 1–20 ranges, each { start, end? }, where an omitted end means the rest of the file. Ranges may overlap, and each returned segment has its own audio_url. For many ranges off a talking-head MP4, run audio detach once, then split.

output.format is wav by default (pcm_s16le, sample-exact) or mp3 (smaller; re-adds priming padding at every edge). Keep wav when the file will be joined again or drives lip-sync. Produced audio is ≤ 1800 s.

curl -X POST https://api.sume.com/v1/timeline-1.0/audio \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: timeline-audio-concat-001" \
  -d '{
    "operation": "concat",
    "parts": [
      { "url": "https://media.sume.com/artifacts/artf_demo/line1.wav" },
      { "url": "https://media.sume.com/artifacts/artf_demo/line2.wav", "source_in": 0.1, "duration": 1.8 }
    ]
  }'

How do I get the results, and how are they billed?

Neither tool has its own GET route. Poll GET /v1/jobs/:id/status and GET /v1/jobs/:id/result. A compose result is kind: timeline_compose with video_url (a new artf_) and duration_seconds; drop that MP4 into Timeline 1.0 video[]. On the hosted MCP server the flows are timeline_compose → jobs_wait → jobs_result and timeline_audio → jobs_wait → jobs_result. Writes need idempotency_key, and mcp:write under OAuth.

Both are billed flat per job; compose is flat because video.duration may be omitted until the worker probes. There is no provider inference, only worker ffmpeg. Rates are on API pricing; the docs say to confirm them live in GET /v1/catalog.

Why was my compose or audio job refused?

  • compose_image_region_wrong_axis: left / right on a horizontal split, or top / bottom on a vertical one.
  • audio_concat_requires_parts, audio_concat_takes_no_url, audio_concat_takes_no_ranges: a concat without parts, or with split fields.
  • audio_split_requires_url, audio_split_requires_ranges, audio_split_takes_no_parts: a split missing url or ranges, or carrying parts.
  • audio_range_end_before_start: a range end ≤ start.
  • audio_parts_channel_mismatch: concat parts do not share a channel layout.
  • unsupported_media_source / source_not_found: an off-host or dead URL.
  • 400 for provider or ffmpeg keys such as filtergraph, ffmpeg_args, codec, or crf.

Sources

Related posts

Written by Sume