> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pyai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Cast expressive voice rendering

> Discover live Cast capabilities, direct a script into performance lines, preview delivery, and submit a durable long-form render.

Cast controls delivery across a script: emotion, intensity, and performance
units. Use Speak when you already know the exact text and only need ordinary
speech synthesis. Use Cast when the delivery itself is part of the artifact.

All Cast API paths live under `/v1/cast/*` and require `cast:render`.

## 1. Read live capabilities

Start every editor or automation from the capability response:

```bash theme={null}
curl https://api.pyai.com/v1/cast/capabilities \
  -H "Authorization: Bearer $PYAI_API_KEY"
```

A typical response looks like this. The live list is authoritative; emotions
and voices are filtered by account policy:

```json theme={null}
{
  "voices": ["stock_dorit_en_us"],
  "emotions": ["excited", "warm", "deep", "empathy", "neutral"],
  "intensity_tiers": ["subtle", "balanced", "bold"],
  "languages": ["en"]
}
```

Do not assume every voice from the general catalog works on Cast, and do not
hardcode an emotion that this response did not advertise.

## 2. Direct a script

Give Cast plain text. Non-empty lines are preserved as performance units; plain
paragraphs are split by sentence:

```bash theme={null}
curl https://api.pyai.com/v1/cast/direct \
  -H "Authorization: Bearer $PYAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "We made it.\nNow let us show them what comes next."
  }'
```

Cast returns directed lines under `lines`:

```json theme={null}
{
  "lines": [
    {
      "text": "We made it.",
      "emotion": "neutral",
      "intensity": "balanced"
    },
    {
      "text": "Now let us show them what comes next.",
      "emotion": "neutral",
      "intensity": "balanced"
    }
  ]
}
```

Every `emotion` and `intensity` in that response is one Cast capabilities
advertises, so a directed line can be sent straight to `/v1/cast/speech` or
`/v1/cast/render_jobs` unchanged.

Treat that result as an editable draft. Your application can let a producer
change any advertised emotion or intensity before rendering.

## 3. Preview one line

Render one directed line synchronously while tuning delivery:

```bash theme={null}
curl https://api.pyai.com/v1/cast/speech \
  -H "Authorization: Bearer $PYAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Now let us show them what comes next.",
    "voice": "stock_dorit_en_us",
    "emotion": "warm",
    "intensity": "balanced"
  }' \
  --output preview.wav
```

The response is WAV audio. Confirm `stock_dorit_en_us` (or any other id) is in
the live `voices[]` list first. A voice outside Cast capabilities returns
`422 unsupported_voice`; refresh capabilities instead of retrying the same
request.

## 4. Render a long project

Use the Cast workspace in the console to create and edit a project. The durable
API submission takes that project's `cast_project_id`, one compatible voice,
and the final directed lines:

```bash theme={null}
curl https://api.pyai.com/v1/cast/render_jobs \
  -H "Authorization: Bearer $PYAI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: cast-demo" \
  -d '{
    "cast_project_id": "cast_project_...",
    "voice": "stock_dorit_en_us",
    "lines": [
      {
        "text": "We made it.",
        "emotion": "warm",
        "intensity": "balanced"
      },
      {
        "text": "Now let us show them what comes next.",
        "emotion": "warm",
        "intensity": "balanced"
      }
    ]
  }'
```

The API returns `202` with `job_id`, `status`, and a `status_url` to poll.
Monitor render progress there, and download the completed artifact from the
Cast project in the console.

Reuse the same `Idempotency-Key` only when retrying the identical render. A
different body with the same key returns `409 idempotency_conflict`.

## Failure handling

* `403 forbidden`: the key lacks `cast:render`.
* `403 cast_emotion_unavailable`: the requested emotion is not enabled for the
  account; render controls from capabilities.
* `422 unsupported_voice`: choose a voice returned by Cast capabilities.
* `422 unsupported_emotion` / `422 unsupported_intensity`: the direction is not
  in the advertised palette. Refresh capabilities; do not retry the same body.
* `429 rate_limit_exceeded`: honor `Retry-After`.
* A long-form submission failure does not make a preview unsafe to retry; use a
  new logical idempotency key only when the render content changes.

<CardGroup cols={2}>
  <Card title="Cast API reference" href="/api-reference">Exact capability, direction, preview, and render schemas.</Card>
  <Card title="Speak" href="/guides/speak-overview">Use ordinary synthesis when you do not need directed performance.</Card>
  <Card title="Voice catalog" href="/api-reference">Browse stock and designed voices, then confirm Cast compatibility.</Card>
  <Card title="Errors and limits" href="/errors-and-limits">Stable request errors, idempotency, and retry behavior.</Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.