---
title: "Set the trailer's camera motion."
method: POST
path: "/trailers/{jobId}/camera"
tags: ["Trailer"]
---

# Set the trailer's camera motion.

`POST /trailers/{jobId}/camera`

## Request

- Base URL: `https://docs.sleeperhit.studio/api/v1`
- URL: `https://docs.sleeperhit.studio/api/v1/trailers/{jobId}/camera`
- Auth: HTTP bearer

Sets the camera motion: `{ handheld?, moves?: [{ unit, move, shot?, target?, from?, zoom? }], off? }` — `handheld` sways every held shot; a move names its beat by `unit` (the beat's `beatIndex`), its shot by number from the read, `move` `snap` (onto `target`, `zoom` up to 1.5) or `reframe` (from `from` to `target`), or `none` to clear the beat; one move per beat; beats not listed keep theirs; `off: true` turns it all off. Refused (400, one sentence naming the fix): a beat with no cut, a shot number it does not have, a shot whose camera already moves or is handheld, a shot where someone speaks on camera, a shot too short for the move, a face not in the shot's cast. Camera motion gives the held shots an operator, the way a documentary camera moves: a handheld sway on the shots that were rendered still (off, subtle, medium; never on a shot whose camera already moves or was filmed handheld, and never so far that a speaking mouth leaves the frame), and at most one move per chapter or beat, on a held shot where nobody speaks on camera: a snap zoom onto a reaction (up to 1.5×, right after a line) or a slow reframe from one face to another already in the frame. It is free, and laid when the master is assembled, as the first step of the film finish and before any type. The sound and timing are never touched. Allowed in any state: before a master the choice is kept for the first assembly; in review, finishing or complete the master is assembled again with it (`reassembling`). Requires `Idempotency-Key`. Scope: `story:write`.

## Headers

- `Idempotency-Key` string, required

## Request body

- CameraSetRequest: A handheld strength, the units' moves (units not listed keep theirs; one move per unit), or `off: true` to turn the camera off.
  - `handheld` 'off' | 'subtle' | 'medium'
  - `moves` object[]
    - `unit` integer, required: The chapter's `chapterIndex` / the beat's `beatIndex`.
    - `move` 'snap' | 'reframe' | 'none', required: `snap`, `reframe`, or `none` to clear the unit's move.
    - `shot` integer: The shot number from the read (required unless `none`).
    - `target` string: The face the move lands on (required unless `none`).
    - `from` string: A reframe's first face.
    - `zoom` number: A snap zoom's zoom (default 1.4).
  - `off` boolean

## Response `200`

The camera motion now in force.

- CameraSetAck: Camera motion gives the held shots an operator, the way a documentary camera moves: a handheld sway on the shots that were rendered still (off, subtle, medium; never on a shot whose camera already moves or was filmed handheld, and never so far that a speaking mouth leaves the frame), and at most one move per chapter or beat, on a held shot where nobody speaks on camera: a snap zoom onto a reaction (up to 1.5×, right after a line) or a slow reframe from one face to another already in the frame. It is free, and laid when the master is assembled, as the first step of the film finish and before any type. The sound and timing are never touched.
  - `handheld` 'off' | 'subtle' | 'medium', required: The handheld sway on the held shots (`off` when the camera is off).
  - `on` boolean, required: True when the camera does anything (a handheld strength or a move).
  - `units` object[], required: Every chapter (deck) or beat (trailer), in order.
    - `unit` integer, required: The chapter's `chapterIndex` or the beat's `beatIndex`: the key a set names it by.
    - `unitNumber` integer, required: Its place, 1-based: the number a writer sees.
    - `hasCut` boolean, required: It has a rendered cut (a move can be chosen only then).
    - `state` 'on' | 'waiting' | 'partly' | 'off', required: `on`: the last master laid it as chosen; `waiting`: chosen, not in a master yet; `partly`: the sway was laid but the move was left out (`moveReason`); `off`: the camera does nothing to it.
    - `move` CameraMove, nullable, required: A chapter's or beat's move, as chosen.
      - `move` 'snap' | 'reframe', required: `snap`: a snap zoom onto a reaction; `reframe`: a slow reframe from one face to another.
      - `shot` integer, required: The shot's number in the cut, 1-based.
      - `target` string, required: The face the move lands on.
      - `from` string, nullable, required: A reframe's first face.
      - `zoom` number, nullable, required: A snap zoom's zoom.
    - `moveState` 'applied' | 'dropped', nullable, required: What the last master made of the move, when laid for this choice.
    - `moveReason` 'moving' | 'speaks' | 'too_short' | 'not_found' | 'no_clean_framing' | 'letterboxed', nullable, required: Why a dropped move was left out.
    - `staleMove` boolean, required: The move named a shot a recompile removed: ignored until chosen again.
    - `sentence` string, nullable, required: What the review says for it.
    - `shots` object[], required
      - `number` integer, required: The shot's number, 1-based, as the cut plays them: what a set names.
      - `startSeconds` number, required
      - `endSeconds` number, required
      - `framing` string, required
      - `camera` 'held' | 'moving' | 'handheld', required: `held`: rendered still (it takes the sway and a move); `moving`: the camera already travels; `handheld`: filmed handheld (never swayed again).
      - `speaksOnCamera` boolean, required: Someone says a line on camera in it (no move goes on it; its sway keeps the speaker's mouth in frame).
      - `cast` string[], required: Who the call renders: who a move may aim at.
      - `action` string, required: What happens in it.
  - `handhelds` object[], required
    - `id` 'off' | 'subtle' | 'medium', required
    - `label` string, required
    - `description` string, required
  - `moves` object[], required
    - `id` 'snap' | 'reframe', required
    - `label` string, required
    - `description` string, required
  - `snapZoom` object, required
    - `min` number, required
    - `default` number, required
    - `max` number, required
  - `description` string, required
  - `reassembling` boolean, required: True when the master is assembled again with the new choice (a rendered deck or trailer; free).
  - `masterHold` TrailerMasterHold: On a rendered trailer with no screenplay, a blank beat holds its video: nothing rebuilds it until each blank beat is a capture beat or a title card, or removed (then it is rebuilt, free); the answer's `masterHold` names the beats and the fix, and the export is refused with it (`trailer_capture_only`).
    - `blankBeats` integer[], required: The blank beats, numbered from 1.
    - `message` string, required: The sentence to show: the beats, that the video keeps playing, and the fix.

## Other responses

- `400`: Request payload failed validation. The error message names the offending field.
- `401`: Missing, malformed, revoked, or expired API key.
- `403`: API key is valid but lacks the scope, is blocked by IP allowlist, or the account is disabled.
- `404`: The named project or source does not exist or is owned by another account.
- `429`: Rate limit for this API key + IP + route family was exceeded.
- `500`: The API failed unexpectedly. Safe to retry with the same Idempotency-Key.

---

[API](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api.md) · [All operations](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/llms.txt) · [OpenAPI document](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/revisions/64c1102ccb2f?raw)
