Documentation
Roll & Cut turns a sentence into a finished, narrated, multi-shot film by running one pipeline: scripting, a production bible for continuity, reference-driven images, voice casting, motion, and a scored soundtrack.
Getting started
Choose a plan and create an account, then head to your new productionpage, describe what you want, set it up, and roll it. Progress, the storyboard and the final download appear on that production’s own page as the worker processes your job.
API reference
Everything the app does is available programmatically. Create a key under Settings → Developers and send it as a bearer token:
curl https://your-domain/api/v1/productions \
-H "Authorization: Bearer ck_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1234" \
-d '{
"prompt": "A 30-second cinematic documentary about the deep ocean",
"recipe": {
"length": "30_60s",
"aspectRatio": "9:16",
"genre": { "mode": "pinned", "styleId": "cinematic-documentary" }
}
}'API access is included with the Studio plan. Requests are rate limited per key, and every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset.
Endpoints
POST /api/v1/productions | Start a production from a prompt + recipe. Returns 201 immediately — rendering is asynchronous. |
POST /api/v1/productions/estimate | Price a recipe in credits before spending anything. |
GET /api/v1/productions | Your productions, newest first (?limit=&offset=). |
GET /api/v1/productions/:id | Status, progress, credits, and which review gate is waiting, if any. |
GET /api/v1/productions/:id/assets | Download URLs for the final video, its separate audio tracks, and the storyboard. |
POST /api/v1/productions/:id/cancel | Stop a production. Returns 202: a render already in flight stops at its next stage boundary. |
GET /api/v1/styles | The style library with each preset's full spec, plus length tiers. |
GET /api/v1/models | Selectable models per pipeline stage. |
GET /api/v1/tiers | Plan catalogue, plus your own entitlements and credit balance. |
The recipe
A production is configured by a recipe: length, aspectRatio (16:9/9:16/1:1), audio toggles (music, narration — video always renders), genre, reviewGates, and models. Every field is optional; omitted fields take platform defaults. Anything your plan doesn't allow is rejected with plan_limit and the specific reasons — call GET /api/v1/tiers to see your limits up front.
Genre
genre decides how the style library is used, and it is a tagged object rather than a plain id so that an incomplete choice cannot be expressed:
{"mode":"auto"}— the default, and the right answer nearly always. The screenwriter reads your prompt and picks whichever genre fits best. A thin prompt is exactly when this matters most: committing to one clear genre is what makes a vague request into a coherent film.{"mode":"pinned","styleId":"cinematic-documentary"}— that genre is used, whatever the prompt says. CallGET /api/v1/stylesfor the ids and their full specs.{"mode":"custom"}— no house genre. The screenwriter composes a style for this piece specifically, from your prompt, so describe the look you want in the prompt text.
A genre is a complete specification — medium, pacing, shot length, narration mode, music presence, sound design, cast — and every later stage is scored against it, so pinning one changes the whole production rather than tinting it. Every production commits to a genre; there is no mode that skips it, because a piece made in no particular style is a set of unrelated clips. An unknown styleId, a pinned with no id, or a custom carrying one are all rejected as invalid_request.
Credits
Credits are reserved when a production starts and settledto the measured provider cost when it finishes — the difference is refunded, and a failed production is refunded in full. The reservation is deliberately a ceiling, so it is normally larger than the final charge. If your balance can't cover a reservation, create returns 402 insufficient_credits before any work happens.
Idempotency
Send an Idempotency-Key header on create. A retry with the same key returns the original response (with Idempotent-Replay: true) instead of starting a second production. Reusing a key with a different body is a 409 conflict.
Webhooks
Register an endpoint under Settings → Developers to receive production.completed, production.failed, production.awaiting_review, production.insufficient_credits, production.cancelled. Each delivery carries a roll-and-cut-signature: t=<unix>,v1=<hmac> header, where the HMAC-SHA256 is computed over `${t}.${rawBody}`with your endpoint's secret. Verify it, and reject timestamps more than a few minutes old so a captured delivery can't be replayed:
import { createHmac, timingSafeEqual } from "node:crypto";
const parts = new Map(header.split(",").map((p) => p.split("=")));
const t = parts.get("t"), v1 = parts.get("v1");
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const ok =
timingSafeEqual(Buffer.from(expected), Buffer.from(v1)) &&
Math.abs(Date.now() / 1000 - Number(t)) < 300;Respond 2xx to acknowledge. Failed deliveries retry with exponential backoff for roughly fifteen minutes; after that, GET /api/v1/productions/:id remains the source of truth — treat webhooks as a way to avoid polling, never as the only way to learn an outcome.
Errors
Every failure returns { "error": { "code": "…", "message": "…" } }. Branch on code — it is a stable contract (invalid_request, unauthorized, forbidden, not_found, conflict, insufficient_credits, plan_limit, rate_limited, expired, internal_error) — and show message to people. A production that fails after starting is not an error response: it reports a failureCode on the production object instead.
How long files are kept
A production's files are kept for 14 days after it stops — completes, fails, is cancelled, or sits waiting at a review point — and are then deleted. filesExpireAt on the production is the date; filesDeletedAt is set once they are gone, and its assets then answer 410 expired. Download what you want to keep.