---
title: "Refine one trailer beat."
method: POST
path: "/trailers/{jobId}/beats/refine"
tags: ["Trailer"]
---

# Refine one trailer beat.

`POST /trailers/{jobId}/beats/refine`

## Request

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

Edits one trailer beat's plan: what it delivers (`storyBeat`), BOTH sides of its seam — `transitionPrompt` outgoing and `transitionIn` incoming — its framing (`shotType`), motion direction, its staging (`characterAction` — what its people physically do — and `stagingNotes`, which a beat held as one shot is filmed from; '' clears either), its words on screen and their look (`text`: { words, style, tempo, place, enterOn, emphasis }, absent fields kept, null clearing the words of a beat over a picture or a screen; `captionText` is gone and refused), composition, duration, who is in the picture (`visibleCharacters` is the named characters on camera; unnamed people need no listing), where it is set (`location`, stored as a canonical location key), the stage of the trailer's intent it serves (`arcStage`, `advancesQuestion` — plan words that never make a take stale; on a draft the changed plan is queued for plan coverage, `planCoverage`), whether it is a rapid intercut run (`montage` — brief shots cut tight, as short as 1 s; it stales the takes), what the narrator says over it (`narrationText`, at most 600 characters, '' clears it — laid over the picture, so it never stales a take; a blank beat added to a rendered trailer is given its line this way). Who speaks this beat's narration (`narrationSpeaker`): a cast member named on the project, spoken in their canon voice instead of the cut's narrator (at most 160 characters; an empty string or null clears it back to the narrator). Only a beat nobody can watch speak may name one — a `capture` or `text` beat; on a filmed beat a visible speaker's words belong in `spokenLine`, said on camera, and are refused here. An unknown speaker is refused when the cut renders, naming them; the narrator never speaks their line for them. And `shots` — the beat's CUT LIST of shots with hard cuts between them, each with whatever camera move it wants, which is how a beat is made to contain an EVENT rather than an image. A beat names where it is set in its `location`: a canonical location key, the place its location image is made of. A beat may travel: a shot set somewhere else names that place in its own `location` (a key, or a place in plain words), and the render follows it there. A capture beat plays one of the project's app captures (a real screen recording, `ready`) as it was recorded: set it with the beat refine (`compositionKind: 'capture'` and the capture's `captureId`). It plays the whole capture, or only the stretch of it you keep (`captureKeep`: a start and an end, in seconds of the capture); what it plays runs 4 to 30 seconds, so a longer capture plays a kept stretch of it. The capture must be in the trailer's shape. The beat carries no on-screen text (its caption is cleared and none can be set), and is never rendered or retaken, so it costs nothing. A beat that already has rendered shots cannot become one: add a beat for the capture instead. A capture beat's refusals: `captureId` or `captureKeep` without a capture beat, a capture beat with a length or a directed text motion (400), a capture beat's words in any style but `label` (409); a capture not of the trailer's project (404 `capture_not_found`); a capture not ready, playing outside 4–30 s (a capture longer than 30 s plays a kept stretch: `captureKeep`), a kept stretch that does not fit its capture (`trailer_capture_keep_range`), ends before the result of a click it keeps (`trailer_capture_keep_cuts_result`), or in another shape, or a beat that already has rendered shots (409). Allowed in draft, review and complete. Free — it saves the plan and marks what went stale: an edit to what the beat films marks its takes out of date (they stay playable); a text edit marks nothing — the master draws every beat's words, so a rendered trailer's master is stitched again with them, free (`masterRebuild`), and the answer's `text` says how they play; words over a style's limit, or a text card that cannot be read in 6 s, are refused (409, `trailer_text_refused`); render the beat again with `beats/render-clip` (priced). A narration edit on a rendered trailer that narrates is voiced and the master stitched again, free (`reassembling: true`); on a cut with no narrator voice a new line on a beat that names no `narrationSpeaker` is refused (409, `details.refusal.code` `trailer_narrator_missing`: choose a narrator first). Returns `needsRerender`, `planCoverage`, `reassembling` and `previewUrl`. Provide at least one editable field. Requires `Idempotency-Key`. Scope: `story:write`.

## Headers

- `Idempotency-Key` string, required

## Request body

- TrailerBeatRefineRequest: Edit to one trailer beat's plan (provide at least one editable field). Free; an edit to what the beat films marks its takes stale, and rendering it again is the priced render-clip op.
  - `beatIndex` integer, required: Zero-based index of the beat to edit.
  - `storyBeat` string: What the beat delivers dramatically: the source its shots are compiled from.
  - `transitionPrompt` string: The OUTGOING side of this beat's seam — how this beat hands off to the next one.
  - `transitionIn` string: The INCOMING side of this beat's seam — how this beat receives the handoff from the PREVIOUS beat. A seam is two independent fields on two beats (beat N's transitionPrompt, beat N+1's transitionIn) and nothing reconciles them, so both must be written to agree. Rejected, not truncated, above 400 characters.
  - `shotType` string: The beat's framing (a single-shot beat's only framing): shot 1's framing picks the plate rung the render opens on — wide, medium or close. Prefer a canonical token: establishing, wide, medium-wide, medium, two-shot, over-the-shoulder, insert, closeup, extreme-closeup. Rejected, not truncated, above 120 characters.
  - `motionPrompt` string: How the beat moves: what the figures do and what moves in the room.
  - `characterAction` string: What the people in this beat physically do, in order. A beat held as one shot is filmed from it; a beat with a cut list is filmed from its shots' own characterAction instead. When it changes what the beat films, its takes go out of date. Never on-screen text — words on the screen are the beat's `text`. An empty string clears it.
  - `stagingNotes` string: Where the people and things in this beat are and how the picture is arranged. A beat held as one shot is filmed from it; a beat with a cut list is filmed from its shots instead. When it changes what the beat films, its takes go out of date. Never on-screen text. An empty string clears it.
  - `text` TextBeatEdit: A beat's words on screen and their look; absent fields keep what the beat has. A text beat is words plus a style: statement (A theme or a title: one idea, centred, each word rising in out of a soft blur. At most 7 words.); thought (An interior line, a question or a note to self, low on the left, each line rising in. At most 14 words.); typed (Words being typed (a prompt, a search, a line the character writes), letter by letter with a blinking caret. At most 12 words.); reveal (An editorial title or a chapter name: a thin rule draws, then the line wipes on. At most 6 words.); label (Names what the product just did, beside the screen: small capitals on a dark pill. The only style a screen recording carries. At most 4 words.); stat (A number that matters ("3 hours back"): the number counts up, its words rise under it. At most 4 words.); directed (Anything else, moved the way plain words describe it (through the motion direction). At most 20 words.). Absent, a text card is a statement, words over picture a thought, and a screen recording's words a label. Tempo calm, standard or brisk; place centre, lower, upper, left or right; enterOn cut, a narrator word, or the score's downbeat; emphasis marks up to 2 exact words. A text change is free; a rendered cut is rebuilt free.
    - `words` string: The words. Over a style's limit they are refused: "At most N words for a {style}; say it shorter."
    - `style` 'statement' | 'thought' | 'typed' | 'reveal' | 'label' | 'stat' | 'directed': statement: A theme or a title: one idea, centred, each word rising in out of a soft blur. (at most 7 words) thought: An interior line, a question or a note to self, low on the left, each line rising in. (at most 14 words) typed: Words being typed (a prompt, a search, a line the character writes), letter by letter with a blinking caret. (at most 12 words) reveal: An editorial title or a chapter name: a thin rule draws, then the line wipes on. (at most 6 words) label: Names what the product just did, beside the screen: small capitals on a dark pill. The only style a screen recording carries. (at most 4 words) stat: A number that matters ("3 hours back"): the number counts up, its words rise under it. (at most 4 words) directed: Anything else, moved the way plain words describe it (through the motion direction). (at most 20 words) A capture beat's words are a label only.
    - `tempo` 'calm' | 'standard' | 'brisk': How fast the style plays: calm (×1.35), standard, brisk (×0.75).
    - `place` 'centre' | 'lower' | 'upper' | 'left' | 'right': Where the words sit; absent, the style's own place.
    - `enterOn` union: When a beat's words come in: on its cut, on a word the narrator says (its real timing; a beat with no timings, or no such word, comes in on the cut and says so in `notes`), or on the score's downbeat near the cut (none marked: the cut, said in `notes`).
      - object
        - `kind` 'cut', required
      - object
        - `kind` 'voWord', required
        - `word` string, required
        - `occurrence` integer: Which time the narrator says it, from 1.
      - object
        - `kind` 'downbeat', required
    - `emphasis` string[]: Up to two exact words of the line, each appearing once, marked by a line sweeping under them (on a text card).
  - `compositionKind` 'image' | 'text' | 'both' | 'capture': What the beat is made of: footage only, on-screen text only, text over footage, or an app capture played as recorded (`capture`, with `captureId`). A capture beat plays one of the project's app captures (a real screen recording, `ready`) as it was recorded: set it with the beat refine (`compositionKind: 'capture'` and the capture's `captureId`). It plays the whole capture, or only the stretch of it you keep (`captureKeep`: a start and an end, in seconds of the capture); what it plays runs 4 to 30 seconds, so a longer capture plays a kept stretch of it. The capture must be in the trailer's shape. The beat carries no on-screen text (its caption is cleared and none can be set), and is never rendered or retaken, so it costs nothing. A beat that already has rendered shots cannot become one: add a beat for the capture instead.
  - `keepsSound` boolean: A beat plays its takes' own sound (true) or the cut's planned sound (false). A beat that keeps its sound (`keepsSound: true`) plays what its own footage carries in the master: a capture's own recorded track (a table read's voices, the app's own sounds), or a rendered beat's own take sound (a crowd, singers, a room's own sound) instead of the cut's planned silence, room tone and laid lines. It plays over the score: the music dips about 12 dB under it, wherever it sounds. The narration dips it only while a line is spoken, and it comes straight back after; a beat that does not plays the cut's planned sound. A capture only keeps its sound when it has one. Free: in review the master is rebuilt from what it has, and a finished trailer's master is stitched again. A capture beat with no sound track is refused (409). The answer's `masterRebuild` says what follows on a rendered trailer.
  - `scoreUnderOwnSound` 'duck' | 'out' | 'hold': What the music does under its own sound: `duck` lowers it about 12 dB (the default), `out` stops it over a quarter second, `hold` keeps its level. Where a beat plays its own sound (a capture's recorded track, a cut that kept its takes' sound, a chapter's kept scene sound), `scoreUnderOwnSound` says what the music does under it: `duck` (Lower, the default) dips it about 12 dB; `out` (Stop) fades it to silence over a quarter second and back after, for a captured video that carries its own score; `hold` (Keep) leaves it at its level (a spoken line still dips it). `ownSoundGainDb` (−12 to +18, null clears) raises or lowers the beat's own sound itself. The score to picture is composed around it, thinning or stopping there, so a score composed before the change is out of date: score to picture again. Free: in review the master is rebuilt from what it has, and a finished video's master is stitched again.
  - `ownSoundGainDb` number, nullable: Its own sound raised or lowered in the master, in dB from -12 to +18; null or 0 plays it as recorded.
  - `depthPreset` 'screen' | 'flat' | 'subtle' | 'cinematic' | 'showcase' | 'hero' | 'none', nullable: A capture beat's own depth preset: screen, flat, subtle, cinematic, showcase, hero, `none` to play it full frame whatever the trailer's says, or null to follow the trailer's again. Every recording plays flat and full frame (the `screen` look), as a viewer sees their own screen: no window, no turn, no blur and no bounce. The camera cuts in to what each click changed (at most 1.3×, held at least 1.8 s, then a cut back or the beat's own cut), pushes in slowly while the viewer reads (to at most 1.05×), drifts no more than 0.4% a second, never zooms in and back out inside one shot, and holds still while words are on screen. It never moves during a scroll and never crops the cursor. The other presets (flat, subtle, cinematic, showcase, hero) are refused (409 `screens_play_flat`); the cursor and the stage's background can still be tuned. Free. Refused on a beat that is not a capture beat (400). The answer's `masterRebuild` says what follows on a rendered trailer.
  - `depthOverrides` CaptureDepthOverrides: Depth tuning deltas over a preset (M-003): cursor/camera/tilt numbers, each clamped to the preset bounds when read, the stage's look (S3b: a device frame, the background's colours and motion — a project's brand, and how much room is around the app, how round its window's corners are and how deep its shadow falls) and the cursor's look (S3c: arrow, hand or auto, its size, colour, shadow, click bounce and sparks, motion blur) and the tap mark's timing on a phone (BL-1223: how early it appears before a press, how long it holds and fades, and how far its lift's ring grows); stored as deltas only. A preset's look (its device frame, its background and its cursor) stays when a beat plays calmer: a beat moved to a gentler preset so its words stay readable, the still version it is checked against, and the quicker version made when the full one cannot be rendered all keep the look that was asked for and lose only motion (the camera and the tilt). Only reading outranks the look: when the app's words would render smaller than the preset's smallest readable size, the margins shrink first, then the device frame is left off.
    - `stage` object: The stage's look: the device the recording sits in, the background behind it, and how much room is around the app, how round its corners are and how deep its shadow falls.
      - `frame` object
        - `bezelPx` number: On a drawn laptop or phone (BL-1225): how thick its black screen edge is, px at 1080p.
        - `radiusPx` number: On a drawn laptop or phone (BL-1225): how round its screen corners are, px at 1080p (a phone follows its own size).
        - `barShare` number: On a window or browser frame (BL-2537): how tall its title bar or tab strip is, a share of the recording's height (0.05 on a window and 0.075 on a browser today). A laptop, a phone or no frame ignores it.
        - `kind` 'none' | 'auto' | 'window' | 'browser' | 'laptop' | 'phone': The device frame: none, auto (the device that fits the recording: a laptop around a landscape one, a phone around a portrait one), a window bar, a browser bar, a laptop or a phone. A laptop or phone always follows the recording's shape.
        - `tone` 'dark' | 'light'
      - `shadow` object: The window's shadow: which way it falls (BL-464) and how deep it falls (BL-394).
        - `from` 'light' | 'down': Which way the window's shade falls: `down` — today's picture, what an absent key reads as; `light` — away from the stage's lit middle. Omit to leave it as it is.
        - `opacity` number: How deep the window's shadow falls: 0 is none.
        - `blurPx` number: How soft the window's shadow is, px at 1080p (BL-2416).
        - `offsetYPx` number: How far the window's shadow drops below it, px at 1080p (BL-2416).
        - `spreadPx` number: How far the shadow is pulled in under the window, px at 1080p (BL-2416); lower tucks it in.
      - `framing` object: How big the app sits in the frame and where (BL-2416): `whole` inside the stage's room (sized by `padding`), or `close`, a product shot filling `fill` of the frame and moved off centre by `offsetX`/`offsetY`. Setting `fill` or an offset without `kind` frames the app close. A close framing that would cut off the words a beat is about plays whole for that beat.
        - `kind` 'whole' | 'close'
        - `fill` number: The share of the frame the recording fills; past 1 the device bleeds off the frame.
        - `offsetX` number: Left (negative) or right (positive) of centre, a share of the frame.
        - `offsetY` number: Up (negative) or down (positive) of centre, a share of the frame.
      - `background` object
        - `stops` string[]: The background's colours, darkest first.
        - `motion` 'still' | 'drift' | 'waves': Whether the background holds still, drifts slowly, or rolls in slow waves.
        - `driftSeconds` number: On a drifting stage: how long the drift takes to travel its lap, seconds (4–60; 24 today). Lower is faster.
        - `driftReach` number: On a drifting stage: how far it drifts, as a share of the frame (0–0.1; 0.04 today). Higher travels further.
        - `auroraWash` number: On an aurora stage (BL-2539): how far the base colour washes toward the second colour (0–0.6; 0.2 today). A gradient or solid stage ignores it.
        - `auroraGlow` number: On an aurora stage: how strong its glows are, a multiplier on each (0–2; 1 today, 0 none).
        - `auroraEdgeFalloff` number: On an aurora stage: how much of the centre's light the frame's edges keep (0.3–1; 0.62 today, 1 no falloff).
        - `source` 'brand' | 'scene': Where the stage stands: `brand` — the project's brand gradient (as every stage stood until now); `scene` — the beat's own room, its location's accepted plate blurred and darkened behind the window. Omit to leave it as it is.
      - `reflection` boolean: True draws a faint mirror of the window's own bottom edge on the stage underneath it; false draws none. Omit to leave it as it is.
      - `padding` number: How much room is around the app: 0 fills the frame with the window, 0.6 leaves the widest margin.
      - `radiusPx` number: How round the window's corners are, px at 1080p: 0 is square.
      - `pageLoadDim` number: How far a dark app's white page load is dimmed (BL-1168): 0 leaves it exactly as recorded, 0.85 (today) the deepest.
      - `address` object: The browser frame's address pill (BL-2536). Omit either to keep today's.
        - `labelMax` integer: How many characters of a long address the pill keeps, the last one an ellipsis (64 today).
        - `loadFadeMs` number: How long the page-load fill under the address takes to leave once the page has loaded, ms (200 today; 0 cuts it at once).
        - `markTone` string: The little site letter's tile colour on any barred frame (BL-2534), #rrggbb: held to the nearest of the 32 site-letter tones (same lightness, every hue). Omit it and each site's name picks its own tone.
      - `windowRim` object: The hairline edge around a window on the stage (BL-2535); a laptop or a phone draws its own body edge and takes none. Omit for the automatic hairline.
        - `widthPx` number: How thick the edge is, px at 1080p (absent: about 1 px, one per 600 px of the window's short side).
        - `tone` 'auto' | 'light' | 'dark': `auto` — the stage decides (white on a dark stage, near-black on a light one); `light` — a white edge; `dark` — a near-black edge.
      - `mirror` object: The mirror under the window (BL-1325): how far it reaches, how soft its far edge goes and how strong it starts. Each omitted is today's mirror; `reflection: false` still turns it off.
        - `reach` number: How far the mirror hangs, as a share of the window's height (a third today).
        - `blurPx` number: How soft its far edge goes, px at 1080p (6 today).
        - `opacity` number: How strong it starts at the window's edge (absent: the frame's tone decides — 0.3 on a dark frame, 0.22 on a light one).
    - `cursor` object: The cursor: its look (S3c: never stepped down) and its feel. Sizes and shadow offsets are px at 1080p.
      - `skin` 'arrow' | 'hand' | 'auto': arrow, hand, or auto (the hand over anything clickable, the arrow elsewhere).
      - `sizePx` number: The cursor's height in px at 1080p.
      - `fill` string: The cursor's fill colour.
      - `shadow` object: Its drop shadow: where it sits under the pointer, how soft it is, and how deep (opacity 0 is none).
        - `offsetX` number: Across, px at 1080p.
        - `offsetY` number: Down, px at 1080p.
        - `blurPx` number: How soft, px at 1080p.
        - `opacity` number
      - `click` object: A click: bounce 0–2 (a squash, an overshoot and a spring back; 0 is the press alone) sparks (the click's flourish: short strokes off the hand's fingertip, a soft ring under the arrow; false draws neither) ripple 0–1 (how strongly the arrow's ring shows; 0 is none, 1 the default) and sparkSize 0.5–2 (how big the hand's spark burst is, scaled as one; 1 the default).
        - `bounce` number
        - `sparks` boolean
        - `ripple` number
        - `sparkSize` number
      - `motionBlur` number: How much it smears along its path while it moves fast: higher is a longer, softer smear, 0 is none (0–1).
      - `smoothing` number: How softly it eases into and out of each move: higher is a smoother, rounder path, lower a crisper one (0–1).
      - `pressScale` number: How far it dips on every click, as a share of its own size: lower is a deeper press, 1 is no dip (0.8–1).
      - `pressMs` number: How long it stays pressed on every click, ms: shorter is a snappier press (40–250).
      - `glideMinMs` number: The least time it takes for any move, even a short hop, ms: higher keeps it from jumping onto a button (150–600).
      - `glideMaxMs` number: The most time it takes for any move, even across the whole screen, ms: higher is slower travel, lower if it drags; never under the least time for a move (300–1000).
      - `preClickPauseMs` number: How long it rests on its target before it clicks, ms: 0 clicks the moment it arrives (0–400).
      - `leanDeg` number: How far the pointer itself leans into its sideways travel, degrees (0 stands it upright). The screen's own lean into the move is tilt.lean.
      - `lift` number: How far the pointer lifts off the page while it travels (its shadow spreads, softens and fades in flight): 0 keeps the shadow glued to the page, 1 is the full lift and the default (0–1).
    - `touch` object: The tap mark a phone frame draws instead of the cursor (BL-1223): how early it appears before the press, how long it holds, how long it fades and how far the lift's ring grows past the disc. The three times are milliseconds; ringGrowth is a share of the disc's radius.
      - `leadMs` number
      - `holdMs` number
      - `fadeMs` number
      - `ringGrowth` number
    - `typing` object: The glow over the field being typed into (BL-2497): how early it arrives before the first keystroke and how long it lingers after the last. A secret field's veil eases in over the same lead.
      - `glow` object
        - `leadMs` number: How early the glow arrives before the typing, ms (150 today).
        - `tailMs` number: How long the glow lingers after the typing, ms (400 today).
        - `strength` number: How big and bright the glow is, as a share of today's halo (1 today; 0 draws none; BL-2481).
    - `follow` object: How closely the camera follows the pointer between shots (BL-2496): how far off-centre the pointer may sit before the camera drifts, how far past that the drift firms up, and how firmly it corrects.
      - `deadZone` number: How far off-centre the pointer may sit before the camera drifts after it, a share of the half-frame (0.5 today).
      - `ramp` number: How far past that the drift eases from its gentlest to its firmest, a share of the half-frame (0.15 today; 0 firm at once).
      - `strength` number: How firmly the drift corrects toward the pointer, × today's (1 today; 0 never follows).
    - `clickImpact` object: How a click presses and rocks the picture (BL-1286).
      - `ms` number: How long the press plays, ms (260 today).
      - `dip` number: How deep the picture dips (0.24 today).
      - `rise` number: How far it rebounds past the press (0.16 today).
      - `rockDeg` number: How far the picture rocks at a tap near its edge, degrees (1.9 today).
      - `sparkMs` number: How long a click's flourish (the hand's sparks, the arrow's ring) flies and fades, ms (420 today; 0 draws none; BL-1322).
    - `clickSound` object: How the clicks sound (BL-1246).
      - `levelDb` number: How loud a click sounds, dBFS (−12 today).
      - `underVoiceDb` number: How loud under a spoken line, dBFS — never louder than levelDb (−30 today).
      - `minGapMs` number: The least time between two clicks heard, ms (120 today).
    - `resultTrace` object: The ring around what a click opened (BL-1285): its clock after the result appears, held drawMs ≤ holdMs ≤ endMs, and its stroke.
      - `drawMs` number: Drawn in by, ms (350 today).
      - `holdMs` number: Held at full until, ms (900 today).
      - `endMs` number: Gone by, ms (1300 today).
      - `ringPx` number: Its stroke, px at 1080p (2 today; 0 draws none).
    - `caption` object: How the stage caption (and the callout pointing from it) arrives and leaves (BL-1287).
      - `enterDelayMs` number: The wait after the picture lands, ms (250 today).
      - `enterMs` number: How long it takes to arrive, ms (450 today).
      - `rise` number: How far it rises into place, a share of its size (0.35 today).
      - `exitMs` number: How long it takes to leave, ms (300 today).
      - `shortBeatMs` number: A beat shorter than this captions at once, ms (1600 today).
      - `shortEnterMs` number: A short beat's caption arrives over this, ms (300 today).
    - `closer` object: The close-up (BL-2417): how a beat framed closer for reading (the video review found its words hard to read) shows the app. Changes nothing on a beat that is not framed closer.
      - `fill` number: The recording's share of the stage (past 1 it crops into the app's own margins, only with no device frame, never past a word).
      - `pushScale` number: The slow push toward the words while they are read; 1 is no push (the off value).
      - `fitMargin` number: The air left around the words, a share of the frame.
    - `camera` object
      - `move` 'auto' | 'yawLeft' | 'yawRight' | 'pitchUp' | 'pitchDown' | 'dolly' | 'hold': The camera move (S3d) through the wide shots between the punch-ins: yawLeft or yawRight (the left or right side turned away), pitchUp or pitchDown (the top or bottom edge tipped away), dolly (square, a slow push in), hold (square and still), or auto (the trailer varies it: never the same move twice in a row, about a quarter to a third held square). The click punch-ins play on every move.
      - `maxScale` number
      - `readHold` object
        - `scale` number
        - `ms` number
        - `minGapMs` number
      - `orbit` number
      - `driftPerSecond` number
      - `dollyScale` number
      - `openWideMs` number: How long the recording holds the whole screen wide at the start, ms (0: it starts already punched in).
      - `closeWideMs` number: How long the recording holds the whole screen wide at the end, ms (0: it ends without coming back out).
      - `leastBetweenWidesMs` number: How close two wide moments may sit, ms: under this the two shots merge into one or one is dropped.
      - `rampBaseMs` number
      - `rampPerLnMs` number
      - `tailMs` number: How long the camera rests wide after a read, ms (BL-1266).
      - `mergeGapMs` number: How close two click punch-ins may sit before they plan as one move, ms.
      - `chainGapMs` number: The gap under which two moves chain into a pan instead of cutting, ms.
      - `minHoldMs` number: The least the camera holds a punch-in on what it shows before it may leave, ms (1800 today); a punch-in with no room for it is skipped (BL-2494).
      - `chainPanMs` number: How long a chained pan travels from one move to the next, ms (1000 today; BL-2494).
      - `fitMargin` number: How much air a punch-in leaves around what it holds, a share of the frame on each side (BL-2470): 0 frames it tight to the edge, 0.25 leaves a quarter of the frame as air; 0.12 by default.
      - `followCaret` boolean: Whether a typing punch-in follows the caret (BL-2538): true (the default) slides the framing along the line as the text fills; false holds it on the field's centre for the whole beat.
      - `holdOnResult` boolean: Whether the picture holds a moment on what the beat's last click changed (BL-463): true (the default) stops the recording briefly on the result while the cursor, caption and click sound keep going; false lets it run straight through. The beat's length never changes.
    - `tilt` object
      - `angleScale` number: The 3D turn's strength against the preset's (0 is no turn: no 3D depth at all).
      - `lean` number
      - `hold` 'opening' | 'wide': Where the turn is held (S3d): on the opening only, or through every wide stretch (square again before each punch-in and read).
      - `startDeg` number: The turn's angle in degrees on its move's main axis at the beat's first frame (with `endDeg`, both or neither; a yaw turns at most 25.2°). Replaces the preset's strength × the beat's varied angle.
      - `endDeg` number: The turn's angle in degrees at the beat's last frame, eased from `startDeg` (the same number holds one angle).
      - `rampMs` number: How long the 3D turn takes to ease in and out, ms (700–800 today; BL-2418).
      - `minHoldMs` number: With `hold: wide`, the shortest wide stretch the turn is held through, ms (1600–1800 today); a shorter one stays square (BL-2418).
      - `minEstablishMs` number: The shortest quiet opening (or establish-mark stretch) a turn is laid on, ms (1000–1200 today; BL-2418). It never delays a turn: a turning beat opens already turned (easing in from square after a cut); longer drops the turn from beats whose quiet opening is shorter.
    - `dof` object: The depth of field on a turned screen (S3d): its far side softly out of focus, sharp toward its near edge.
      - `maxBlurPx` number: The far side's blur at full turn, px at 1080p; 0 is none.
      - `focus` number: How much of the turned screen stays sharp, from its near edge inward; 0.8 keeps the most.
    - `spotlight` object: The spotlight while a shot holds on one control (BL-1163): how far the rest of the app dims, how long it takes to dim, and the shortest hold that dims at all.
      - `dim` number: How dark the rest of the screen goes while a shot holds on one control, 0–0.85; 0 is no dim.
      - `rampMs` number: How long the dim takes to come in, ms, 0–600.
      - `minHoldMs` number: The shortest hold that dims at all, ms, 0–2000.
  - `stageCaption` string: A capture beat's stage caption is a few words (at most 60 characters) shown in the stage's margin below the floating window, never over the captured screen. It needs a depth preset: a beat played full frame has no margin. '' clears it. Refused on a beat that is not a capture beat (400), and on one that plays full frame (409: give it a depth preset first).
  - `captureSpeedUp` 'off' | 'gentle' | 'standard' | 'brisk', nullable: A capture beat plays the stretches where the viewer would only wait (the page loading or an answer streaming in, a long run of typing, a long scroll, an idle pause) sped up and eased in and out, while every click, its result and the moments to read play at real speed; the narrator's line still lands on its click, and the beat is never cut shorter than its line. On by default. `captureSpeedUp` sets it for one capture beat: `off` (real time), `gentle` (about 2× through waits), `standard` (the default, about 4×) or `brisk` (about 8×); null puts the beat back on the default. A beat that keeps its recording's own sound plays in real time unless it is set. Refused on a beat that is not a capture beat (400). The answer's `masterRebuild` says what follows on a rendered trailer.
  - `captureId` string: The app capture a capture beat plays: one of the trailer's project's captures, `ready` (list them with GET /story-projects/{projectId}/captures). Sent with `compositionKind: 'capture'`, or alone to swap a capture beat's capture (which drops its kept stretch unless `captureKeep` comes with it). Refused unless what it plays (the whole capture, or the kept stretch) runs 4–30 s, in the trailer's shape.
  - `captureKeep` object, nullable: The stretch of its capture a capture beat plays: `captureKeep` with `startSeconds` and `endSeconds` (seconds of the capture, decimals allowed). The kept stretch must start before it ends, end inside the capture, and run 4 to 30 seconds; the beat then runs that long, and its sound, cursor and camera follow the stretch. Null plays the whole capture again. Changing the beat's capture drops the kept stretch unless you send one with it. Free. A stretch that does not fit the capture is refused (409, `trailer_capture_keep_range`); so is a stretch that ends before the result of a click it keeps (409, `trailer_capture_keep_cuts_result`, with the sentence naming the end that would keep it); on a beat that is not a capture beat (400). The answer's `masterRebuild` says what follows on a rendered trailer.
    - `startSeconds` number, required: Where the kept stretch starts, in seconds of the capture.
    - `endSeconds` number, required: Where it ends, in seconds of the capture: after the start, inside the capture.
  - `intendedDurationSeconds` integer: The beat's length in seconds (4–30). A beat renders as calls of at most 10s each. A capture beat runs the length of what it plays and refuses one: keep a stretch of its capture instead (`captureKeep`).
  - `visibleCharacters` string[]: The NAMED characters in the picture (max 8), by exact project-canon name — each renders from their own identity reference. Unnamed people (a crowd, passers-by) are not listed and need none. Pass [] for a beat with no cast member on screen, e.g. a street or a vista.
  - `featuredCharacter` string, nullable: The beat's lead character (null clears it).
  - `location` string, nullable: Where the beat is set, stored as a canonical location key (pass the key, or a room name, resolved to the room's stored key). A room with no plate yet is legal: the render makes it, priced as a plate line. Null clears it. A beat names where it is set in its `location`: a canonical location key, the place its location image is made of. A beat may travel: a shot set somewhere else names that place in its own `location` (a key, or a place in plain words), and the render follows it there.
  - `shots` TrailerShot[]: THE BEAT'S CUT LIST — shots with HARD CUTS between them, performed inside the beat's render calls. Use it when a beat has to contain an EVENT rather than an image; leave it off for a beat that is one continuous shot. The cuts are FREE — cost is per rendered second, not per cut. A beat names where it is set in its `location`: a canonical location key, the place its location image is made of. A beat may travel: a shot set somewhere else names that place in its own `location` (a key, or a place in plain words), and the render follows it there. Pass [] to clear the list back to a single shot.
    - `shotType` string, required: THIS shot's framing — the point of a cut list is that the framing changes between entries. Shot 1's framing picks the plate rung the render opens on.
    - `storyBeat` string, required: What this shot delivers dramatically: the fragment of the build that happens here, not the beat's whole arc.
    - `cameraMove` string: What the camera does in THIS shot: holds, pushes in, travels, cranes, follows. Keep the move inside this shot: the next one starts on a hard cut.
    - `characterAction` string: What the figure physically does during this shot.
    - `visibleCharacters` string[]: Exact project-canon cast names visible in THIS shot (max 3).
    - `spokenLine` string: Exact words a VISIBLE character speaks during this shot.
    - `spokenLineCharacter` string: Who says `spokenLine` in this shot.
    - `location` string: Where THIS shot is, when it is not the beat's own place. A beat names where it is set in its `location`: a canonical location key, the place its location image is made of. A beat may travel: a shot set somewhere else names that place in its own `location` (a key, or a place in plain words), and the render follows it there.
    - `durationSeconds` number: How long this shot holds, in seconds (at least 2; as short as 1 in a montage beat). Optional: the fitter rescales written rhythms to the beat, and an all-unwritten list is split evenly.
  - `arcStage` 'setup' | 'escalation' | 'turn' | 'closing_question': The stage of the trailer's intent this beat serves: setup, escalation, turn or closing_question. The beats follow the arc in order, every stage is served, and the last beat is the closing_question — plan coverage refuses a plan that breaks it. Plan words: never makes a take stale.
  - `advancesQuestion` string: One sentence: how this beat advances the intent's central question — what it raises, sharpens or makes costlier.
  - `whyItFollows` string: One line, never on screen: why this beat comes after the one before it — the link a viewer sees or hears and what it moves forward. Required on every beat after the first; "" clears it. Plan words: never makes a take stale.
  - `montage` boolean: true makes the beat a rapid intercut run — several brief shots (one to two seconds each) across the people and places its shots name, cut tight; its shots may run as short as 1 s. false makes it hold or travel. It changes how the beat compiles, so its takes go out of date.
  - `narrationText` string: What the narrator says over this beat: one line in the cut's narrator voice, laid over the finished picture (never part of the footage). Heard only when the cut narrates (`audioPolicy: narrate`). Write what the cut means, never what is on screen, and leave it empty on a beat whose character speaks. It never makes a take stale; on a rendered trailer that narrates, the new line is voiced and the master stitched again, free. An empty string clears it.
  - `narrationSpeaker` string, nullable: Who speaks this beat's narration (`narrationSpeaker`): a cast member named on the project, spoken in their canon voice instead of the cut's narrator (at most 160 characters; an empty string or null clears it back to the narrator). Only a beat nobody can watch speak may name one — a `capture` or `text` beat; on a filmed beat a visible speaker's words belong in `spokenLine`, said on camera, and are refused here. An unknown speaker is refused when the cut renders, naming them; the narrator never speaks their line for them.

## Response `200`

The saved beat + a shareable beat preview link.

- TrailerBeatPreview: The saved beat plus a shareable link to preview it.
  - `success` boolean
  - `needsRerender` boolean: True when the beat's rendered takes are now out of date. They stay playable; render the beat again (priced) to bring it in line. A words edit never sets it.
  - `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.
  - `planCoverage` 'queued' | 'current' | 'failed' | 'null', nullable: On a draft plan the edit changed: its plan coverage — `queued` (one run per burst of edits, started once the plan has been left alone for three minutes; the plan approval starts it at once), `current` (this revision is already scored or being scored) or `failed` (it could not be queued; the approval says what is missing). Null outside draft, or when the edit changed nothing.
  - `masterRebuild` 'reassembling' | 'restitching': When the edit changed what a capture beat plays (its capture, the capture taken off, or its sound), what the music does under a beat's own sound (`scoreUnderOwnSound`, `ownSoundGainDb`), or a beat's words, on a rendered trailer: `reassembling` — in review the master is rebuilt from what it has (free); `restitching` — a finished trailer's master is stitched again at its class (free). Absent otherwise.
  - `reassembling` boolean: True when the edit changed what the narrator says on a rendered trailer that narrates: the line is voiced again and the master stitched again, free. Poll the beats read until the new master lands.
  - `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.
  - `ownSoundWarning` string: The beat's narration speaks over its own sound, which dips under the line: say it to the writer (move the line to the beat before, onto a black, or clear it). Advisory, the edit is saved. Absent when they do not overlap.
  - `previewUrl` string: Shareable link to watch this beat's current cut.

## 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.
- `409`: The trailer exists and the request is well-formed, but the trailer, its beat, its cast or its plates are not in a state that allows it (`trailer_state_invalid`), and nothing was reserved: a plan approved outside draft, a take chosen or a beat rendered outside review/complete, an export before the finish, a finish while a call has no selected take, a narrated cut with no narrator, a render the compile blocked, or a finish while the master failed its video coverage (`trailer_video_coverage_failed`, with `details.videoCoverage`). `details.refusal` is `{ code, action, fix }` (see TrailerRefusalDetails): `action` is what unblocks it — `recast_figure`, `add_signature_items`, `recast_voice` or `remake_location_plate` — and `fix` names the MCP tool and CLI command that take it; `details.blocked[]` lists every beat a blocked compile could not render. The identical request succeeds once the trailer is put right.
- `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 warning, 3 info
  - removed the request property `captionText`
  - added the new optional request property `text`
  - added the new `screen` enum value to the request property `depthPreset`
  - 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/beats/refine/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/4534f5d25540?raw)
