---
title: "Move a trailer beat to another position."
method: POST
path: "/trailers/{jobId}/storyboard/move-beat"
tags: ["Trailer"]
---

# Move a trailer beat to another position.

`POST /trailers/{jobId}/storyboard/move-beat`

## Request

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

Moves one beat to another position in the cut; `toIndex` is where the beat ENDS UP. NOT metered — it costs no credits and generates nothing, so there is nothing to quote or confirm. It rewrites the order and carries each beat's rendered takes and cut with it. Returns the new `beatCount`, `movedTo`, and `staleBeats` — the beats whose FOLLOWING beat changed, so their seam direction is out of date; their footage stays playable. A trailer with no screenplay (POST /story-projects/{projectId}/trailers) is edited this way on its draft too, since no planner draws its beats; a screenplay trailer's draft keeps the plan its planner drew. Requires `Idempotency-Key`. Scope: `story:write`.

## Headers

- `Idempotency-Key` string, required

## Request body

- TrailerMoveBeatRequest: Move one trailer beat to another position in the cut.
  - `fromIndex` integer, required: Zero-based index of the beat to move.
  - `toIndex` integer, required: Zero-based position the beat ENDS UP at.

## Response `200`

The reordered cut + the beats now out of date.

- TrailerBeatOrderAck: The result of a beat-order edit. Costs no credits and renders nothing.
  - `success` boolean, required
  - `beatCount` integer, required: How many beats the cut has after the edit.
  - `staleBeats` integer[], required: Beats whose FOLLOWING beat changed, so their seam direction is out of date. Their footage stays playable.
  - `movedTo` integer: Where the moved beat ended up (move only).
  - `insertedBeatIndex` integer: Where the new beat landed (insert only).
  - `text` TrailerTextOutcome: How the beat's words will play once drawn by the master, and the cut's text density rules it breaks.
    - `style` 'statement' | 'thought' | 'typed' | 'reveal' | 'label' | 'stat' | 'directed' | 'null', nullable, required
    - `seconds` number, nullable, required: How long the beat runs (a text card: its read time).
    - `window` TrailerTextWordsWindow, required: When the words are on screen, in seconds from the beat's first frame: they start coming in, are fully in, start going, are gone.
      - `enterAt` number, required
      - `readFrom` number, required
      - `exitAt` number, required
      - `goneAt` number, required
    - `notes` string[], required: What the words could not keep as asked, and the density rules the cut breaks, in the writer's words.
  - `captureOnly` boolean: Insert only: the trailer has no screenplay, so the new beat is never rendered — make it a capture beat or a title card with `beats/refine`; approving or finishing it is refused with `trailer_capture_only` while it is blank.
  - `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.
  - `masterRebuild` 'reassembling' | 'restitching': Remove: the last blank beat of a rendered trailer with no screenplay was removed, so its held video is rebuilt (free): `reassembling` in review, `restitching` when finished. Insert of a text card on a rendered trailer: `restitching`, the master stitched again with it (free). Absent otherwise.

## Other responses

- `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.

## Changes

- **2026-10-20** `4534f5d25540`: 1 info
  - added the optional property `text` to the response with the `200` status

[Operation history](https://skmtc.dev/sleeperhit/apis/sleeper-hit-studio-story-api/changes/trailers/:jobId/storyboard/move-beat/post.md)

---

[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/86e084144cef?raw)
