---
title: "Pin one headshot candidate as the person's face on the cast canon."
method: POST
path: "/story-projects/{projectId}/cast-portraits/choose"
tags: ["Projects"]
---

# Pin one headshot candidate as the person's face on the cast canon.

`POST /story-projects/{projectId}/cast-portraits/choose`

## Request

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

Pins candidate `candidate` (1-based) of design `designId` as the person's headshot on the cast canon, with the prompt it was drawn from: every new episode, deck, trailer and showcase draws them from it. Free (the candidates were paid for). A NEW face clears the canon body shot and character sheet, because they were drawn from the previous face (`cleared`; `previous` keeps the old urls): draw them again with POST /story-projects/{projectId}/cast for this character. Choosing the face already pinned changes nothing. A candidate still being drawn, or that could not be drawn, is a 409 `cast_precondition_failed`.  Requires `Idempotency-Key`. Scope: `story:write`.

## Headers

- `Idempotency-Key` string, required

## Request body

- CastPortraitChooseRequest
  - `designId` string, required
  - `candidate` integer, required: 1-based candidate number from the design.
  - `acknowledgeCastingCoverage` boolean: Choose a headshot its casting coverage FAILED (otherwise 409 `cast_portrait_casting_coverage_failed`, `details.castingCoverage`). Send true ONLY after the writer has seen the failure and chosen it anyway.

## Response `200`

The face pinned, and what it cleared.

- CastPortraitChooseResponse
  - `projectId` string, required
  - `designId` string, required
  - `character` string, required
  - `candidate` object, required
    - `index` integer, required
    - `candidateId` string, required
    - `imageUrl` string, required
  - `cleared` string[], required: Canon pictures drawn from the previous face, cleared by the choice: draw them again with POST /story-projects/{projectId}/cast for this character.
  - `castingCoverageWarning` string, nullable: What casting coverage said about this choice: still running, could not run, another recommended, or chosen past a failure.
  - `previous` object, required
    - `face` string, nullable, required
    - `body` string, nullable, required
    - `sheet` 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`: No portrait design with that id in this project (`portrait_design_not_found`), or the project is not yours (`project_not_found`).
- `409`: The headshot cannot be chosen, and nothing was pinned: `cast_portrait_casting_coverage_failed` when its casting coverage FAILED it (`details.castingCoverage`: the score, the bar, why, the headshot it recommends instead and any revised look; send `acknowledgeCastingCoverage: true` only once the writer has chosen it anyway), or `cast_precondition_failed` for a candidate not drawn yet.
- `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)
