---
name: livoice
description: Put a custom voice on an OpenAI GPT-Live-1 session by swapping the WebSocket URL. Everything else about the GPT-Live protocol is unchanged.
---

# Livoice

Livoice is a proxy in front of OpenAI's GPT-Live-1. Your app talks the normal GPT-Live WebSocket protocol; Livoice converts the model's output audio into a chosen voice inside the stream, adding about 200-250 ms. Interruptions, pauses, backchannels and the model's own turn-taking are untouched because the audio is converted, not regenerated.

## What to change

1. Point the WebSocket at Livoice instead of OpenAI and add the voice id and your Livoice access key as query parameters:

```
wss://api.livoice.dev/v1/live/sessions?voice=<voice_id>&key=<livoice_key>
```

2. Send your OpenAI key in the `X-OpenAI-Token` header instead of `Authorization`. It is forwarded to OpenAI for that session and never stored.

```
X-OpenAI-Token: sk-...
```

3. Keep everything else exactly as for OpenAI: `session.start`, `session.input_audio.append`, `session.output_audio.delta`, transcripts, delegation. Audio is PCM16 at 24 kHz (16 kHz also accepted).

The proxy sets `session.audio.output.voice` to the source voice that best matches the target (for example `cedar` for Trump, `marin` for the tutor) unless you set one yourself. Do not use WebSocket compression (permessage-deflate); it is refused.

## Voice ids

| voice id | who | notes |
|---|---|---|
| `xvc_trump` | Trump parody | demo only; also enables an OpenAI text-model "earpiece" for facts |
| `azuma` | Japanese tutor | older engine, first start about a minute |
| `xvc_maz` | Maz | |
| `xvc_kinu` | Kinu | |
| `passthrough` | OpenAI's own voice | no conversion, for A/B |

## Session flow and timing

- First connection wakes the GPU: about 12 s (about a minute for `azuma`). The socket may close during wake-up; reconnect every 5 s until `session.started` arrives.
- Two concurrent sessions per box. A third gets close code 4005 `busy`.
- Close codes: 4001 missing Livoice key, 4002 missing `X-OpenAI-Token`, 4004 unknown voice, 4005 busy, 4006 free seconds used up (add a card), 4008 unknown or revoked key, 4502 voice server unreachable (retry in a few seconds). Every failed connection also shows on your dashboard under Issues.
- At session end the proxy sends one extra event, `livoice.session.stats`, with the added latency it measured (`added_ms_p50`, `added_ms_p95`).

## Minimal example (Python, websockets)

```python
import asyncio, base64, json, websockets

URL = "wss://api.livoice.dev/v1/live/sessions?voice=xvc_maz&key=LIVOICE_KEY"

async def main():
    async with websockets.connect(URL, additional_headers={"X-OpenAI-Token": "sk-..."}, compression=None) as ws:
        await ws.send(json.dumps({"type": "session.start", "session": {
            "model": "gpt-live-1", "instructions": "Keep replies short.",
            "audio": {"format": {"type": "audio/pcm", "rate": 24000}}}}))
        # stream your mic as 100 ms PCM16 chunks:
        # await ws.send(json.dumps({"type": "session.input_audio.append", "audio": base64.b64encode(pcm).decode()}))
        async for msg in ws:
            e = json.loads(msg)
            if e["type"] == "session.output_audio.delta":
                pcm = base64.b64decode(e["delta"])   # 24 kHz PCM16 in the chosen voice: play it
asyncio.run(main())
```

## Credit

Livoice is prepaid: $0.05 a minute, charged per second. A session stops with close code 4006 when the balance runs out. Check the balance, and buy more when the account owner allows it.

```
GET  https://api.livoice.dev/v1/balance        Authorization: Bearer lv_...
POST https://api.livoice.dev/v1/credit         Authorization: Bearer lv_...   Idempotency-Key: <unique per purchase>
     {"amount_usd": 10}                        (10, 25 or 50)
```

- `/v1/credit` charges the card on file and adds the credit at once. It works only while the owner has auto-recharge on (dashboard, Billing), and within their optional monthly limit.
- Send a new `Idempotency-Key` for each purchase and reuse it when retrying the same purchase: the card is charged once.
- When it can't charge, the reply has `ok: false`, an `error` (`auto_recharge_off`, `no_saved_card`, `monthly_cap_reached`, `card_declined`, `authentication_required`) and a `checkout_url`. Give that link to the account owner to pay by hand.
- With auto-recharge on, the balance also refills by itself when it drops below the owner's threshold, so most agents never need `/v1/credit`.

## Cloning a voice

A clone needs about a minute of clean speech from a speaker who has agreed to it. Sign in and record it at https://livoice.dev/record; the voice is added to your account once it is built. Cloning is free and does not use your minutes.
