---
title: Timeline audio
description: Concat or split Sume-hosted audio into durable media.sume.com files. Sample-domain join, no re-synthesis.
---

> **Current SoT.** Timeline 1.0 audio (`sume/timeline-1.0/audio`, #3516).
> Dest and prod. This mints a **reusable** audio file. A join that is
> only needed inside one render belongs on
> [Timeline 1.0](/models/timeline) `audio.parts[]` — skip this job.

Timeline audio joins Sume-hosted audio into one gapless file
(`operation: concat`) or slices one file into ranges
(`operation: split`) and returns durable `media.sume.com` URLs plus
timings. The join is sample-domain — no re-TTS and no silence at the
seams. The server compiles ffmpeg on the same worker media runtime as
the render (`apps/api/src/routes.ts` `createTimelineV1Audio` /
`submitSumeTimelineAudioJob`).

```text
POST /v1/timeline-1.0/audio
```

There is **no** `GET /v1/timeline-1.0/audio/:id`. Poll the job envelope:

```text
GET /v1/jobs/:id/status
GET /v1/jobs/:id/result
```

Hosted MCP: `timeline_audio` (`packages/mcp-server/src/mcp.ts`). Writes
need `idempotency_key` (and `mcp:write` under OAuth). Flow:
`timeline_audio` → `jobs_wait` → `jobs_result`.

## Concat

Required: `operation: "concat"` and `parts[]` (1–**20**, ordered). Each
part is `{ url, source_in?, duration? }`. Do **not** send `url` or
`ranges` at the top level (`audio_concat_takes_no_url` /
`audio_concat_takes_no_ranges`). All URLs must already be this
workspace’s `media.sume.com` audio. Import first
(`POST /v1/media-imports`). `Idempotency-Key` is required.

Default `mode` is **`async`**. Pass `mode: "sync"` to wait up to
**30 seconds** for a `200` finished job, or get `202` and poll.

```bash
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 }
    ]
  }'
```

Result `kind: timeline_audio` with one `audio_url`,
`duration_seconds`, and `segments[]` (`index`, `start`,
`duration_seconds`) — the concat offsets to re-base Timeline 1.0
`video[].start` against. Use that file as `audio.url` on the render, or
as Avatar 1.0 image-to-video audio.

Parts must share one channel layout (`audio_parts_channel_mismatch`).

## Split

Required: `operation: "split"`, top-level `url`, and `ranges[]`
(1–**20**). Each range is `{ start, end? }` (`end` omitted = rest of
file). Do **not** send `parts` (`audio_split_takes_no_parts`). Ranges
may overlap.

```bash
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-split-001" \
  -d '{
    "operation": "split",
    "url": "https://media.sume.com/artifacts/artf_demo/spine.wav",
    "ranges": [{ "start": 0, "end": 12.4 }, { "start": 12.4 }]
  }'
```

Result `kind: timeline_audio` with `segments[]`, each with its own
`audio_url`. For many ranges off a talking-head MP4: [audio detach](/models/audio-detach)
once, then split here.

## Output format

Optional `output.format`: **`wav`** (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 ≤ **1800** s.

Public rate: **$0.01 flat per job** (`TIMELINE_AUDIO_PUBLIC_PRICING`;
confirm live in `GET /v1/catalog`). No provider inference — worker
ffmpeg only.

## Refusals (stable codes)

| Code | When |
|---|---|
| `audio_concat_requires_parts` | Concat without `parts`. |
| `audio_concat_takes_no_url` / `audio_concat_takes_no_ranges` | Concat plus a split field. |
| `audio_split_requires_url` / `audio_split_requires_ranges` | Split missing `url` or `ranges`. |
| `audio_split_takes_no_parts` | Split plus `parts`. |
| `audio_range_end_before_start` | A range `end` ≤ `start`. |
| `audio_parts_channel_mismatch` | Concat parts do not share a channel layout (worker). |
| `unsupported_media_source` / `source_not_found` | Off-host or dead URL. |
| Provider / ffmpeg keys | 400 — `filtergraph`, `ffmpeg_args`, `codec`, `crf`, friends. |

Off-host URLs (`https://example.com/…`) are rejected at admit. Import first.

## Not this surface

| Need | Use |
|---|---|
| Audio track of one video as wav / mp3 | [Audio detach](/models/audio-detach) |
| Join only for one render | [Timeline 1.0](/models/timeline) `audio.parts[]` |
| Sequence several clips | [Timeline 1.0](/models/timeline) |
| Still + video in one frame | [Timeline compose](/models/timeline-compose) |
| Speech-to-text | `POST /v1/stt-1.0/generate` |

## Related

- [Timeline 1.0](/models/timeline)
- [Timeline compose](/models/timeline-compose)
- [Audio detach](/models/audio-detach)
- [Media inputs](/workflows/asset-library)
- [Jobs and results](/workflows/jobs-and-results)
- [MCP tools and gates](/mcp/tools-and-gates)
