---
title: "Cast a story film's people in one step (metered — TWO CALLS, price first)."
method: POST
path: "/story-projects/{projectId}/cast-film"
tags: ["Projects"]
---

# Cast a story film's people in one step (metered — TWO CALLS, price first).

`POST /story-projects/{projectId}/cast-film`

## Request

- Base URL: `https://docs.sleeperhit.studio/api/v1`
- URL: `https://docs.sleeperhit.studio/api/v1/story-projects/{projectId}/cast-film`
- Auth: HTTP bearer

Casts a product project's STORY FILM: everyone who speaks in its screenplay (`script`, default the newest), plus anyone named in `people` who is only seen, at most 3 in all. Each person without a headshot gets 3 headshot candidates on the cast headshot model; each who speaks without a voice gets 3 candidate voices. Give each person a `look` and a `voice` unless their cast entry or persona already has one; `persona` copies a Bible persona into the cast with them. TWO CALLS: without `confirmed` it PRICES everything and writes nothing (`quotedOnly: true`, each person's `headshots` and `voices`, `credits`, `balanceAfter`, `sufficient`, `note`); with `confirmed: true` it saves the people on the cast canon (their look and age) and starts every design (`people[].headshot.designId` — poll GET .../cast-portraits/designs?designId= — and `people[].voice.designId`). Every candidate drawn is charged, chosen or not; choosing one headshot and one voice per person is free. Refused before the price: 409 `cast_precondition_failed` with reason `story_film_cast_needs_direction` (`details.missing` names who needs a look or a voice), `story_film_cast_too_many`, `story_film_cast_no_people` or `product_cast_needs_story_film`; 409 `project_not_product` on a story project; 404 `script_not_found`.  Requires `Idempotency-Key`. Scope: `artifact:publish` (billable).

## Headers

- `Idempotency-Key` string, required

## Request body

- CastStoryFilmRequest
  - `script` string: The film's screenplay: a script or version id. Omitted: the project's newest story film.
  - `people` object[]: Who to cast, with how each looks and sounds. Everyone who speaks in the screenplay is cast either way.
    - `name` string, required: As the film names them: a cue ("MAE") or a name in its action ("Nora").
    - `look` string: How they look on camera.
    - `age` string
    - `voice` string: How they sound.
    - `gender` string
    - `persona` string: The Bible persona they grew from, copied into the cast with them.
  - `confirmed` boolean: Omit (or false) to get the price without writing or spending anything. true saves the people and starts every design, only after the writer has seen the price.

## Response `200`

The price (no `confirmed`) or the started designs.

- CastStoryFilmEnvelope: Quote: each person with the headshots and voices a confirmed call draws for them, and the price. Confirmed: each person's started designs (`headshot.designId`, `voice.designId`) or why one could not start.
  - `quotedOnly` boolean, required
  - `projectId` string, required
  - `scriptUploadId` string, required
  - `title` string, required
  - `people` object[], required
    - `name` string, required
  - `credits` integer, required
  - `costLabel` string, nullable
  - `balanceAfter` integer: Quote only.
  - `sufficient` boolean: Quote only.
  - `note` string, required

## Other responses

- `400`: Request payload failed validation. The error message names the offending field.
- `401`: Missing, malformed, revoked, or expired API key.
- `402`: Not enough Studio Credits to reserve this job (`insufficient_credits`), and nothing was reserved. `details` is InsufficientCreditsDetails: `required` and `available`, and `autoTopup` when auto top-up is on but did not cover it — `pending` (retry in a moment), `over_cap` (raise the monthly spending cap), `too_small` (buy a bigger pack) or `failed`.
- `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`: Casting cannot run on this project yet, and nothing was quoted or drawn: nobody castable, nobody with a description to draw from, no passing screenplay coverage, or no image model bound (`cast_precondition_failed`, `details.reason` names which) — or the project itself has not reached casting (`project_precondition_failed`, `details.stage` names the stage to finish). The message says what to do.
- `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/4534f5d25540?raw)
