# Cartesia with LiveKit

> Cartesia in a LiveKit agent: through our plugin, or through LiveKit's Cartesia plugin with a changed URL.

## With our plugin

Cartesia’s Sonic voices with the settings of LiveKit’s Cartesia plugin. `TTS`’s `sample_rate` is 8000, 16000, 22050, 24000, 44100 or 48000.

```shell
pip install livekit-plugins-tts-cache-router
```

**agent.py**

```python
from livekit.plugins import tts_cache_router as tcr

# The API key comes from TTS_CACHE_ROUTER_API_KEY.
tts = tcr.TTS(
    provider=tcr.Cartesia(
        model="sonic-3",
        voice="f786b574-daa5-4673-aa0c-cbe3e8534c02",
        language="en",
    ),
)
```

Pass `tts` to `AgentSession(tts=tts)`. Set `TTS_CACHE_ROUTER_API_KEY` in the agent’s environment, and store your Cartesia key under [Provider keys](https://ttscacherouter.dev/dashboard/provider-keys). `TTS`’s own arguments are on [the plugin’s page](https://docs.ttscacherouter.dev/livekit.md#tts).

### `tcr.Cartesia(...)`

- `model` (`str`, Default `"sonic-3"`): `"sonic-3.6"`, `"sonic-3.5"` or `"sonic-3"`, or a dated snapshot of one, e.g. `"sonic-3.6-2026-08-27"`.
- `voice` (`str`, Default `"f786b574-daa5-4673-aa0c-cbe3e8534c02"`): A Cartesia voice id, from Cartesia’s library or your own voices. The default is Katie, as in LiveKit’s plugin.
- `language` (`str`, Default `"en"`): The language of the text, e.g. `"en"` or `"hi"` (`"en-GB"` is sent as `"en"`, as LiveKit’s plugin sends it). The languages differ by model.
- `speed` (`float | None`, Default `None`): Speaking rate, 0.6–1.5. None: Cartesia’s default (1.0).
- `emotion` (`str | list[str] | None`, Default `None`): Guide the delivery (English), e.g. `"calm"`, `"sad"` or `"angry"`; a list uses its first, as LiveKit’s plugin does. None: Cartesia reads the emotion from the text.
- `volume` (`float | None`, Default `None`): Loudness, 0.5–2.0. None: Cartesia’s default (1.0).
- `pronunciation_dict_id` (`str | None`, Default `None`): A Cartesia pronunciation dictionary’s id. Sentences cached before you edit the dictionary keep their audio until your cache retention expires them.
- `word_timestamps` (`bool`, Default `True`): Word-by-word transcripts from Cartesia's word timings.

Every accepted value, and the languages per model: [Models and options](https://docs.ttscacherouter.dev/reference/models.md#cartesia).

### Changing settings

Replies that start afterwards use the new settings:

```python
tts.update_options(emotion="calm", speed=1.1)
```

## With LiveKit's Cartesia plugin

Keep `livekit-plugins-cartesia` and point it at the router: your router API key in place of your Cartesia key, and the router’s address.

```shell
pip install livekit-plugins-cartesia
```

**agent.py**

```python
import os

from livekit.plugins import cartesia

tts = cartesia.TTS(
    model="sonic-3",
    voice="f786b574-daa5-4673-aa0c-cbe3e8534c02",
    language="en",
    api_key=os.environ["TTS_CACHE_ROUTER_API_KEY"],
    base_url="https://api.ttscacherouter.dev/cartesia",
)
```

`base_url` is the only URL to change: the plugin derives its WebSocket URL from it. Streaming, `synthesize()` and word timestamps work as before.

The router splits the reply into sentences and synthesizes each on its own, through either plugin: each sentence is a take of its own rather than a continuation of the one before, and the pauses between sentences are shorter than in one Cartesia context. When the user interrupts, the sentence being synthesized finishes at Cartesia and is cached; the rest of the reply isn’t sent. The other differences from Cartesia’s own messages are on [Cartesia API drop-in](https://docs.ttscacherouter.dev/clients/cartesia.md#websocket).

Requests, responses and errors keep Cartesia’s own shape; the endpoints are on [Cartesia API drop-in](https://docs.ttscacherouter.dev/clients/cartesia.md).

## Which to choose

Both use your org's cache and your stored provider key, and sound the same.

- **Our plugin** reports only the characters Cartesia billed in `TTSMetrics` (cache hits count zero), and switches provider with `update_options`.
- **Cartesia’s plugin** is the smallest change to an agent that already uses it. Its metrics count every character, cached or not; [Usage](https://docs.ttscacherouter.dev/dashboard.md#usage) has the real split.
