---
name: ochra-cut
description: "Edit the user's own video footage into a finished video with Ochra Cut, through the Ochra connector: upload, analysis, edit documents, previews and the final render. Use when the user wants their clips cut into a reel, a trimmed talking-head video, a captioned edit or any video made from their own footage."
---

# Ochra Cut

## What Cut is and how a cut goes

Ochra Cut edits the customer's own footage into a finished video. You do the thinking — plan the edit with the customer and write it down as an edit document. Ochra does the hands-on work — taking in the footage, analysing it, rendering previews and the final video — and charges the customer's credits for it.

A cut goes like this:

1. `get_cut_guide` — this guide. Costs no credits.
2. `start_cut(title, canvas)` — the canvas is one of 9:16 (1080×1920), 16:9 (1920×1080), 1:1 (1080×1080), 4:5 (1080×1350). It answers with a `cut_id` and an upload link.
3. Footage in. Give the customer the upload link: it opens on a phone or a computer, needs no sign-in, and lasts a day. For a video already in their library, or a share link, use `add_footage`. In Claude Code you can upload files from disk yourself (the "Uploading from Claude Code" topic).
4. `analyze_footage(cut_id)` — a task. Then `get_cut` reads the result: each file's length, a transcript with word times, shot changes, silences, loudness and contact sheets. `view_frames` shows exact stills.
5. Plan the edit with the customer, then write the edit document and call `preview_cut` — it checks the document and shows a still or two. Fix what it reports and preview again.
6. `render_cut` — a task. The finished video lands in the customer's library; show it with `show_media`. For changes, write a new document and render again.

Tell the customer what a step costs before you run it, and only run the steps they asked for. A cut keeps its footage, analyses and every document it checked, so a later chat picks up where this one stopped: `get_cut` with no arguments lists the cuts, and with a `cut_id` it shows one, its latest document included on request.

### Making it good

- Read before you cut. Page through the transcript with `get_cut` (`transcript: { footage_id, from, to }`), and look at the contact sheets and shot changes; `view_frames` shows the exact moment you are about to cut on.
- Reels from clips: open on the strongest moment, not the first one. Keep shots short (one to three seconds for a fast reel), cut on movement or on a shot change the analysis found, and let the music set the pace. Put a title card or a hook in the first two seconds.
- Talking heads: cut out the silences the analysis lists and the false starts you can read in the transcript, cutting at word edges from the transcript's word times. Caption with a `data-captions` block rather than writing words by hand — the timing comes from the transcript and stays right.
- B-roll and voice-over: the customer's other connected tools can make shots, music and speech. They are ordinary generations at their usual prices — say the price and get a yes first — and each output's `asset_id` goes into the document's media map.
- Keep words clear of the edges. On a 9:16 video, phone apps cover the top tenth and the bottom fifth: keep titles and captions between them.
- Preview the moments that matter — the title, a caption, a cut, the end — at most 6 per preview, and look at the stills before you render.

## Connecting

Cut's tools come with the Ochra connector, as a set of their own (each title starts "Ochra Cut:"), on every connection of an account that has bought credits — no switch turns them on. If you do not see `start_cut`: a connection that may only look lists no `start_cut` (the customer connects again and allows “Use your credits”); an account that has not bought credits gets Cut once it has; otherwise start a new chat.

- Claude (web, desktop, phone): Customize → Connectors → + Add → Add custom connector, with the URL `https://www.ochra.io/api/mcp`, then sign in to Ochra and allow it.
- Claude Code: `claude mcp add --transport http --scope user ochra https://www.ochra.io/api/mcp`, then `/mcp` → Ochra → Authenticate.

Cut is for accounts that have bought credits. Everything about it is on https://www.ochra.io/cut.

## Uploading from Claude Code

The upload link from `start_cut` is `https://www.ochra.io/cut/new?cut=<cut_id>&t=<token>`. The same token lets you upload from a terminal: pass it as `?t=<token>` on every call below. A file goes up in parts; each part is PUT straight to storage.

```sh
BASE=https://www.ochra.io/api/cut/uploads
T=<token>   # the t= value from the upload link
F=trip.mov  # the file

# 1. Start: name, size in bytes, type.
curl -s "$BASE?t=$T" -H "content-type: application/json" \
  -d "{\"name\":\"$F\",\"bytes\":$(wc -c < "$F" | tr -d ' '),\"mime\":\"video/quicktime\"}"
# → { "upload_id", "part_bytes", "part_count", … }

# 2. URLs for up to 8 parts at a time (numbered from 1):
curl -s "$BASE/$UPLOAD/parts?t=$T" -H "content-type: application/json" -d '{"numbers":[1,2,3]}'
# → { "parts": [{ "n", "url", "bytes" }], "headers": { … } }

# 3. PUT part n: bytes (n-1)*part_bytes onward, part_bytes long (the last part is the rest).
dd if="$F" bs=$PART_BYTES skip=$((N-1)) count=1 2>/dev/null | \
  curl -s -X PUT "$URL" -H "content-type: application/octet-stream" -H "x-upsert: false" --data-binary @-

# 4. Finish. A 409 lists the parts to send again in error.meta.missing.
curl -s "$BASE/$UPLOAD?t=$T" -H "content-type: application/json" -d '{}'
# → { "footage_id", … }
```

A part that answers "already exists" was sent before — count it as done. `GET $BASE/$UPLOAD?t=$T` says which parts are there, so an interrupted upload carries on where it stopped. Part URLs last two hours: ask for them as you go.

## The edit document

An edit document is JSON: `version` (always 1), `duration_s` (the video's length in seconds), `media` (the files it uses, by a key you choose), `html`, and optionally `css` and `script`.

### A complete example

A 15-second 9:16 reel: a title card that rises in, five seconds of beach footage, seven and a half seconds of a talking clip with its own sound and captions from its transcript, and music under all of it. Swap the ids for the cut's own footage ids (`get_cut`) and the `asset_id` of a track the customer made or owns.

```json
{
  "version": 1,
  "duration_s": 15,
  "media": {
    "beach": {
      "footage": "0f3b6d2a-5c1e-4a8b-9d7f-2e6c4b1a9f30"
    },
    "talk": {
      "footage": "7a2c9e41-3b8d-4f6a-a1c5-9e0d2b7f4c18"
    },
    "music": {
      "asset": "c4e81b07-9d2a-4c3f-8e6b-1a5d7f9c2e64"
    }
  },
  "html": "<div id='title' class='card' data-start='0' data-duration='2.5'><h1>Goa, day one</h1></div>\n<video id='beach' src='media:beach' data-start='2.5' data-duration='5' data-media-start='12.5' muted playsinline></video>\n<video id='talk' src='media:talk' data-start='7.5' data-duration='7.5' data-media-start='41' data-has-audio='true' playsinline></video>\n<div id='subs' class='subs' data-captions='talk' data-style='pop' data-start='7.5' data-duration='7.5' data-media-start='41'></div>\n<audio id='music' src='media:music' data-start='0' data-duration='15' data-volume='0.3'></audio>",
  "css": "video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }\n.card { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: #10131a; }\n.card h1 { margin: 0 80px; font-family: 'Montserrat'; font-weight: 900; font-size: 120px; line-height: 1.05; color: #fff; text-align: center; animation: rise 0.8s ease-out both; }\n@keyframes rise { from { opacity: 0; transform: translateY(60px); } to { opacity: 1; transform: none; } }\n.subs { font-family: 'Montserrat'; --cut-cap-color: #ffffff; }"
}
```

### The frame

`html` is what is inside the frame. The frame itself — its size (the cut's canvas), its length (`duration_s`), its frame rate (30) — is written by Ochra; do not write one. A frame of your own is replaced (its id, class and style are kept, so your CSS still applies), and `duration_s` is the only length that counts.

### Timing

- An element with `data-start` (seconds) is shown from then for `data-duration` seconds, and hidden outside that window. A `<div>` needs both; a picture without `data-duration` shows for 3 seconds.
- `data-start` may also name another element: `"title"` means when it ends, `"title + 0.5"` half a second after. A captions block needs plain numbers.
- Stack later elements on top of earlier ones with CSS (`position: absolute`, `z-index`).

### Media

- Each key in `media` is `{ "footage": "<footage_id>" }` (footage of this cut — `get_cut` lists the ids) or `{ "asset": "<asset_id>" }` (a file in the customer's own library: a generated shot, music or speech, or an upload). Anything else is refused as not found.
- Use a key as `src="media:<key>"` on `<video>`, `<audio>` or `<img>`. Footage must be analysed before it can be used.
- On a `<video>`, `data-media-start` is where in the footage the clip begins, and `data-playback-rate` its speed (0.1 to 10). A video is silent unless it has `data-has-audio="true"`. `data-volume` (0 to 1) sets the level of a sound.
- Captions: `<div data-captions="<key>" data-style="pop" data-start="…" data-duration="…" data-media-start="…">` is filled with timed words from that footage's transcript — give it the same start, length, in-point and speed as the clip it captions. Styles: pop, clean, karaoke. Style them through a class on the block: its `font-family`, and the variables `--cut-cap-size`, `--cut-cap-color` and `--cut-cap-highlight` (karaoke).

### Motion

- CSS animations and transitions, and `element.animate()`, run on the video's clock: a CSS animation starts when the nearest element with `data-start` (itself included) appears.
- three.js: `import * as THREE from "three"` in `script` (a module). Draw on the `hf-seek` event, from `event.detail.time` — never from a clock or `requestAnimationFrame`, because the video is drawn frame by frame. For anything that takes time to prepare, pass its promise to `event.detail.waitUntil(promise)`. A video that draws 3D costs double (see Prices).
- GSAP is not available.

```html
<canvas id='scene' width='1080' height='1920' data-start='0' data-duration='6'></canvas>
```
```js
import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('scene'), antialias: true });
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(40, 1080 / 1920, 0.1, 100);
camera.position.z = 6;
const knot = new THREE.Mesh(new THREE.TorusKnotGeometry(1, 0.32, 160, 24), new THREE.MeshStandardMaterial({ color: 0xffb347 }));
scene.add(knot, new THREE.AmbientLight(0xffffff, 0.6));
const light = new THREE.DirectionalLight(0xffffff, 1.4);
light.position.set(3, 4, 5);
scene.add(light);
window.addEventListener('hf-seek', (event) => {
  const t = event.detail.time; // seconds into the video
  knot.rotation.set(t * 0.6, t * 0.9, 0);
  renderer.render(scene, camera);
});
```

### Not allowed

- Nothing from the internet: no http(s) or `//` addresses in `src`, `href`, `srcset` or `poster`, no CSS `url()` or `@import`, no `<script src>`, `<iframe>`, `<object>` or `<embed>`, and no `import` but `three`.
- Fonts: Noto Sans, Noto Sans Devanagari, Inter, Montserrat, Outfit, Nunito, Oswald, League Gothic, Archivo Black, Space Mono, IBM Plex Mono, JetBrains Mono, EB Garamond, Playfair Display, Source Code Pro, Noto Sans JP, Roboto, Open Sans, Lato, Poppins. Any other family is drawn in Noto Sans, and a document may name at most 32 such families; Hindi text uses Noto Sans Devanagari.

`preview_cut` reports problems with a code, a sentence and the element's id — the first 20 of them, and how many more there are. Fix them and preview again before you render.

## Limits

- The finished video: at most 3 minutes, 1080p and 30 frames a second.
- Each footage file: at most 2 GB and 20 minutes; video only.
- At most 20 files in a cut, 20 cuts open at once, and 10 GB of footage kept across an account.
- An edit document: at most 128 KB of html, css and script, and 40 media.
- At most 8 captions blocks in a document, covering at most twice the video's length of footage in all (data-duration × playback rate, added up), and never more than 6 minutes.
- At most 32 font families beyond the ones listed in the document topic.
- No GSAP, and nothing from the internet. The frame — size, length, frame rate — is written by Ochra.
- At most 6 moments in one preview or one view_frames call; each answer carries at most 2 pictures (several stills share one picture, left to right).
- A cut and its footage are kept for 30 days after it was last changed. Finished videos stay in the library.

## Prices

- Footage analysis: 2 credits per started minute of footage, at least 2 per analysis.
- Preview: 1 credit per preview.
- Final video: 2 credits per started 15 seconds, up to 3 minutes at 1080p.
- Final video with 3D (WebGL) graphics: 4 credits per started 15 seconds.
- Starting a cut, adding footage, reading the guide, checking a cut and viewing frames cost no credits.

Analysis and renders are held when they start and charged on what was actually measured — the footage's real length, the video's real length — never more than was held. Pass `max_credits` with the most the customer agreed to; a hold above it is refused and nothing is held. A preview whose document fails its checks is refused before anything is held, so it costs no credits.
An analysis holds each uploaded file's length as the upload page measured it, plus a little. A file whose length could not be read is held by its size, and a share link as the longest a file may be: up to 40 credits a file. `max_credits` must cover that hold, not only the expected charge — with a small balance, analyse fewer files per call. A file that runs longer than its hold is refused and its credits returned.
