---
title: "Pin an existing voice on one person of the project cast, in an episode or not."
method: PATCH
path: "/story-projects/{projectId}/cast-voice"
tags: ["Projects"]
---

# Pin an existing voice on one person of the project cast, in an episode or not.

`PATCH /story-projects/{projectId}/cast-voice`

## Request

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

Pins `voiceId` (with `voiceName`, `provider` and `gender`, each read from your voice library when omitted) on the cast canon entry of `character` — by canon name, alias or script cue — anyone on the project cast, including a person no screenplay names yet whom a deck or trailer gives a line. Every new episode, deck, trailer and showcase speaks them with it, and an episode that later names them inherits it instead of casting a second one. A Series Bible character the canon does not hold yet is added to it. Never changes a read: `reads` lists the finished table reads that still speak this person with another voice — re-voice them with POST /artifacts/{artifactId}/revoice (priced). `previous` is the canon voice it replaced; set it again to go back. A NEW voice from a description is POST .../cast-voice/design (priced) then POST .../cast-voice/choose. Refused with 409 `voice_retired` for a voice on a provider that is retiring its voices, and 404 `character_not_found` when nobody in the cast canon or Series Bible answers to `character`. Free. Scope: `story:write`.

## Request body

- CastVoiceSetRequest
  - `character` string, required: The person: canon name, alias or script cue. Anyone on the project cast, in an episode or not; a Series Bible character the canon does not hold yet is added.
  - `voiceId` string, required: The voice to pin, by its id at its provider (GET /voices lists yours).
  - `voiceName` string: The name the voice is shown under. Default: your voice library's name for it.
  - `provider` 'cartesia' | 'elevenlabs' | 'openai' | 'deepgram' | 'hume': The provider that speaks the voice. Default: the library's, else the configured one. A provider retiring its voices is refused.
  - `gender` string

## Response `200`

The voice pinned, what it replaced, and the reads still on another voice.

- CastVoiceSetResponse
  - `projectId` string, required
  - `character` string, required: The cast canon person the voice is pinned on.
  - `voice` object, required
    - `voiceId` string, required
    - `voiceName` string, required
    - `provider` string, required
    - `gender` string, nullable, required
  - `previous` object, nullable, required: The canon voice this replaced (null: they had none, or nothing changed). Set it again to go back.
    - `voiceId` string, required
    - `voiceName` string, nullable
    - `provider` string, nullable
    - `gender` string, nullable
  - `changed` boolean, required: False when the person already had this voice: nothing was written.
  - `reads` object[], required: Finished reads that still speak this person with another voice. Re-voice each with POST /artifacts/{artifactId}/revoice (priced).
    - `artifactId` string, required
    - `title` string, nullable, required
    - `voiceId` string, nullable, required
    - `voiceName` string, nullable, required
  - `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.
- `403`: API key is valid but lacks the scope, is blocked by IP allowlist, or the account is disabled.
- `404`: Nobody in the project's cast canon or Series Bible answers to that name (`character_not_found`), or the project is not yours (`project_not_found`). Nothing was changed or charged.
- `409`: A voice its provider has retired is in the way (`voice_retired`), so nothing was queued, charged or changed. `details.voices` names each one; migrate them (POST /voice-migrations), then retry.
- `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/86e084144cef?raw)
