---
title: Video trim
description: Cut a [start, end) range out of one Sume-hosted clip into a new MP4. Material preparation, not timeline placement.
---

> **Current SoT.** Media L2 range cut (`sume/video-trim-1.0`, #5953). Dest
> and prod. This is **not** clip inspection — that is
> [video inspect](/models/video-inspect). It is **not** assembly —
> sequencing, transitions, and the audio spine stay on
> [Timeline 1.0](/models/timeline).

Video trim 1.0 takes **one** workspace `media.sume.com` clip plus a range
and returns a **new** MP4 artifact holding only `[start, end)`. The source
is untouched. The server compiles ffmpeg on the worker media runtime
(`apps/api/src/routes.ts` `createVideoTrimV1` /
`submitSumeVideoTrimJob`).

```text
POST /v1/video-trim
POST /v1/models/sume/video-trim-1.0/runs   # same body, no extra `model` field
```

There is **no** `GET /v1/video-trim/:id`. Poll the job envelope:

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

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

## Create a trim

Required: `video_url` (this workspace’s `media.sume.com` artifact or
asset), `start` (seconds, ≥ 0), and **exactly one** of `end` or
`duration`. There is no open-internet fetch — import first
(`POST /v1/media-imports`). `Idempotency-Key` is required.

Optional: `precision`, `audio`, `output` (exact only), plus the usual
`mode` / `webhook_url` / `wait_timeout_seconds` communication fields.

Default `mode` is **`async`** (`readCommunicationOptions`). 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/video-trim \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: video-trim-001" \
  -d '{
    "video_url": "https://media.sume.com/artifacts/artf_demo/talk.mp4",
    "start": 2,
    "duration": 8
  }'
```

A successful submit returns a job (`request_id` is the job id). When
`result_ready`, `GET /v1/jobs/:id/result` is `kind: video_trim` with
`video_url` (new `artf_`, never the source), `duration_seconds`,
`actual_start_seconds`, `precision`, `audio`, `output`, and optional
`warnings[]`. Drop that MP4 into `timeline_create` `video[]` with
`source_in` 0.

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

## Program

| Field | Effect |
|---|---|
| `start` | In-point, seconds from the source start. Required. |
| `end` | Out-point, seconds. Exactly one of `end` / `duration`. Past the source it **clamps** and the result warns `trim_clamped_to_source`. |
| `duration` | Length of the cut, seconds. `0.2`–`900`. Exactly one of `end` / `duration`. |
| `precision` | `exact` (default): frame-accurate re-encode (`libx264`, `yuv420p`). `keyframe`: stream copy; the cut may start a GOP early — re-base against `actual_start_seconds`. |
| `audio` | `keep` (default) or `drop`. Exact remuxes kept audio as AAC. |
| `output` | Optional `{ width, height, fps }` conform on the way out. **exact only.** Width/height 256–2160; `fps` `24` \| `25` \| `30` \| `60`. Omit to inherit the source. |

Caps (from `packages/api-contract/src/index.ts`): source ≤ **1800** s;
output ≤ **900** s; output ≥ **0.2** s.

## Refusals (stable codes)

| Code | When |
|---|---|
| `video_trim_range_required` | Neither `end` nor `duration`. |
| `video_trim_range_conflict` | Both `end` and `duration`. |
| `video_trim_range_empty` | `end` ≤ `start`, or the range is longer than 900 s. |
| `video_trim_output_requires_exact` | `output` with `precision: "keyframe"`. |
| `ffmpeg_fields_rejected` | Client sent `vf` / `filter` / `ffmpeg` / `cmd` / `codec` / `crf` / friends. The server compiles ffmpeg. |
| `unsupported_media_source` | `video_url` is not on the Sume media host. |
| `source_not_found` | Dead or foreign `media.sume.com` URL. |
| `unsupported_media_type` | HEAD is not a video. |
| `source_duration_exceeded` | Source longer than 1800 s (worker). |

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

## Not this surface

| Need | Use |
|---|---|
| Probe / stills / optional STT | [Video inspect](/models/video-inspect) |
| Audio track as a durable wav / mp3 | [Audio detach](/models/audio-detach) |
| Sequence several clips | [Timeline 1.0](/models/timeline) |
| Pixel pass (dim / crop) | [Video filter](/models/video-filter) |
| Exact frame at `t`, source size | [Video frames](/models/video-frames) |

## Related

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