---
title: "List a project's cast voice designs."
method: GET
path: "/story-projects/{projectId}/cast-voice/designs"
tags: ["Projects"]
---

# List a project's cast voice designs.

`GET /story-projects/{projectId}/cast-voice/designs`

## Request

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

The project's cast voice designs, newest first (at most 50), each with its candidates, their preview urls, and the candidate chosen (`chosen`, or null). `?character=` narrows to one person (by name, alias or cue). Free. Scope: `story:read`.

## Query parameters

- `character` string

## Response `200`

The designs.

- CastVoiceDesignListResponse
  - `designs` CastVoiceDesign[], required
    - `designId` string, required
    - `projectId` string, required
    - `character` string, required
    - `description` string, required
    - `createdAt` string, date-time, required
    - `candidates` object[], required
      - `index` integer, required: The number POST .../cast-voice/choose takes.
      - `label` string, required
      - `previewId` string, nullable, required: The take's preview id at the voice designer (ElevenLabs Voice Design); null for a candidate drawn before the designer changed, which cannot be chosen.
      - `voiceId` string, nullable, required: The voice the candidate became once chosen (cloned from its take); null until it is chosen.
      - `provider` 'cartesia' | 'elevenlabs' | 'openai' | 'deepgram' | 'hume', required: The provider that speaks the candidate once chosen: every designed voice is cloned into it.
      - `previews` CastVoiceDesignPreview[], required: The candidate's take.
        - `text` string, required: The words the take says: the design line, then the preview lines.
        - `url` string, required: Playable MP3 of the take.
        - `scriptUploadId` string, nullable, required: The screenplay the line is from; null for `sampleText`.
        - `entryIndex` integer, nullable, required
    - `chosen` object, nullable, required
      - `index` integer, required
      - `voiceId` string, nullable, required: Null for a design chosen before the designer changed.
    - `coverage` CastingCoverage: CASTING COVERAGE of a cast voice design: a model listened to every candidate take blind, beside the voices already cast in the project, and scored each against the brief and the Series Bible. It runs by itself, free to the writer, when a key character's design is drawn. Choosing a candidate in `failingCandidates` is refused unless the writer chooses it anyway; a report still running, one that could not run, or an unavailable listener never holds a choice.
      - `status` 'missing' | 'generating' | 'complete' | 'failed', required
      - `kind` 'voice', required
      - `projectId` string, required
      - `designId` string, required
      - `character` string, required
      - `keyCharacter` boolean, required: A principal (by the Series Bible or by line count): judged by itself when a design is drawn.
      - `available` boolean, required: False when no provider can listen right now.
      - `gateThreshold` number, required
      - `vetoDimensions` string[], required
      - `distinctnessThreshold` number, required: Speaker-embedding similarity at or above which two voices count as the same speaker (`casting_coverage_policy`, 0.88 by default).
      - `recommended` integer, nullable, required: The best candidate that cleared the bar, on its own.
      - `cast` object, nullable, required: The design's place in the CAST PLAN: across every open design of the project, one candidate each so that no two — and none against a chosen voice — measure as the same speaker. `pick` is the candidate the set takes for this design; `conflicts` the better ones it passed over and who they sound like. Null without a complete, measured report.
        - `pick` integer, nullable
        - `feasible` boolean: True only when `pick` keeps every voice apart AND clears its own bar. False: no valid set — design again; `note` names the colliding pair.
        - `conflicts` object[]
          - `candidate` integer
          - `character` string
          - `similarity` number
        - `designs` integer
        - `note` string
      - `failingCandidates` integer[], required: Choosing one needs `acknowledgeCastingCoverage: true`.
      - `summary` string, required: One sentence to lead with.
      - `trigger` 'auto' | 'manual' | 'null', nullable, required
      - `reportId` string, nullable, required
      - `reportVersion` integer, nullable, required
      - `rubricVersion` integer, nullable, required
      - `model` string, nullable, required
      - `progressDetail` string, nullable, required
      - `failureMessage` string, nullable, required: Why the run stopped, as one plain sentence for the writer — never a provider's or the database's raw wording. Populated when `status: failed`.
      - `report` object, nullable, required
        - `kind` 'voice', required
        - `candidates` CastingCandidateCoverage[], required
          - `candidate` integer, required: The number POST .../cast-voice/choose takes.
          - `rank` number, nullable, required: Median place across the runs (1 = best).
          - `ranks` integer[]
          - `score` number, nullable, required: The weighted score the bar reads (0–10).
          - `passed` boolean, required
          - `recommended` boolean, required
          - `gateReasons` string[], required
          - `flags` string[], required: Short flags to lead with: failures first, then warnings.
          - `dimensions` object, required: emblematicFit, truthfulness, periodRegister, accentHonesty, artifacts, wordAccuracy, distinctness (null: not judged).
          - `transcript` string, nullable: What the take was heard to say, transcribed without the script.
          - `wordAccuracy` number, nullable: Share of the scripted words heard as written (0–1).
          - `misheard` string[]
          - `heardAs` object, nullable
            - `gender` string
            - `age` string
            - `accent` string
          - `strengths` string
          - `problems` object[]
            - `atSeconds` number
            - `note` string
          - `closestCastVoice` object, nullable
            - `character` string
            - `note` string
          - `speakerSimilarity` object[]: MEASURED speaker-embedding similarity (cosine 0–1) to every voice it was compared with, closest first (empty: embeddings unavailable).
            - `character` string
            - `source` 'chosen' | 'recommended'
            - `similarity` number
          - `soundsLike` object, nullable: A chosen voice of another character this candidate measures as the same speaker as (at or above `distinctnessThreshold`): it fails on its own.
            - `character` string
            - `similarity` number
          - `excerpt` object, nullable: The strongest 15–20 s of the take, on sentence boundaries.
            - `startSeconds` number
            - `endSeconds` number
        - `ranking` integer[], required: Every candidate, best first.
        - `recommended` integer, nullable, required
        - `runnerUp` integer, nullable, required
        - `close` boolean, required: The runner-up sat close to the recommendation: hear both.
        - `allFail` boolean, required
        - `redesign` object, nullable, required
          - `why` string
          - `revisedBrief` string: Design again from this (POST .../cast-voice/design with `description`).
        - `verdict` string, required
        - `references` object[], required
          - `character` string
          - `source` 'chosen' | 'recommended'
          - `designId` string
          - `candidate` integer
        - `judging` object, required: How it was reached: each run (its order, ranking, scores and cost), why it stopped, the extra-run spend, the fingerprint, and what was carried.
      - `createdAt` string, date-time, nullable, required
      - `updatedAt` string, date-time, nullable, required

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

---

[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)
