Docs
Livoice sits in front of OpenAI's GPT‑Live‑1 and converts the model's output audio into a voice you choose, inside the stream. Your code keeps talking the GPT‑Live protocol.
Add it to your coding agent
One command, then tell your agent to make the swap. Works with Claude Code, Cursor, Codex and about twenty other agents that read the same skill format.
No skills CLI? Paste this instead
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
- 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>
- Send your OpenAI key in the
X-OpenAI-Tokenheader instead ofAuthorization. It is forwarded to OpenAI for that session and never stored.
X-OpenAI-Token: sk-...
- 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 untilsession.startedarrives. - 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)
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/creditcharges 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-Keyfor 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, anerror(auto_recharge_off,no_saved_card,monthly_cap_reached,card_declined,authentication_required) and acheckout_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.
Questions: [email protected]