Skip to main content
Building with an AI agent? POST /v1/preview is the fast inner loop: validate for free, glance at a frame or a whole-timeline contact sheet, then only call POST /v1/renders once it looks right.

How preview works

Preview reuses the exact same render pipeline and renderer as POST /v1/renders — same engine, same JSON schema — just restricted to a slice of the timeline and downscaled. What you see in a preview is exactly what the full render produces at that timestamp.
  1. Submit POST /v1/preview with a render_request composition.
  2. Choose what you want:
    • dry_run: true — validate only, no render, returns instantly.
    • at: <seconds> — a single frame.
    • window: { from, to } — a short clip, with audio.
    • contact_sheet: { ... } — a grid of frames sampled across the timeline.
  3. Get a response immediately200 for a dry run, no job (a contact-sheet dry run also includes the sampling plan, still with no job). Any non-dry-run preview — frame, window, or contact sheet — returns 202 with a job queued.
  4. Poll like any renderjob.id (prefix render_) works with GET /v1/renders/{id}. Preview jobs are free: excluded from your usage totals and from GET /v1/renders.

Free validation (dry_run)

Set dry_run: true to run Framelane’s linter without spending a render: duplicate element IDs, dangling or self-referencing transitions, an empty timeline, an element or caption that starts after the composition ends, and — if you pass target_duration_sec — a timeline that misses your target length.
ok is false only when a violation is severity: "error" — a missed target_duration_sec, for example, is a "warning" and never blocks you.

Frame and window previews

Pass at for a single frame, or window: { from, to } for a short clip (audio trimmed and muxed for free). Both render at a small draft width by default (480 px; raise width up to 3840 for a sharper look) and dispatch a real, cheap render job.
Poll job.id via GET /v1/renders/{id} exactly like a full render.
at and window are mutually exclusive. A window.to past the end of the composition is clamped down to the composition’s duration rather than rejected — you may get a shorter clip than requested, never an error. A window.from at or past the composition’s duration is rejected, with 422 invalid_request.

Contact sheets: see the whole edit in one image

Set contact_sheet to get back a single grid PNG sampling frames across the timeline — enough to check an entire edit in one glance (or one vision call), without downloading and scrubbing a video.
Framelane samples at meaningful moments — the first and last frame, where each element starts, each transition, and each caption’s first word — then fills any remaining budget uniformly (bare { "detail": "balanced" } on a 10-second composition budgets ~10 cells; max_cells caps that, shown here at 4 to keep the example short). The sheet field describes the plan immediately (even on a dry_run, before any render happens):
Combine with window to sample only part of the timeline instead of the whole thing. Once the render completes, job.output.url is a single PNG — columns × cellWidth pixels wide, rows × cellHeight tall — with each cell’s frame and burned-in label ("0:04 element_start") matching sheet.cells[i] in order.

Request reference

Endpoints

Good to know

  • Preview jobs cost nothing and are excluded from GET /v1/renders and GET /v1/workspace/usage — build as many as you need while iterating.
  • A preview is pixel-identical to the equivalent full render at the same timestamp, modulo resolution — the renderer takes no different code path for a preview, so what you approve (once scaled up) is what you’ll get.
  • Preview requests never accept Idempotency-Key — every call creates a new job.
  • If an element references media you haven’t finished uploading yet, a real preview (dry_run: false) returns 422 asset_not_ready; a dry_run: true validation tolerates it and lints the rest of the composition anyway.