Realtime relay
Open a metered OpenAI-Realtime voice session through bitHuman, over WebSocket or WebRTC.
Overview
A voice conversation (speech in, speech out) with no OpenAI key of your own: the relay opens an OpenAI-Realtime voice session through bitHuman and bills it in bitHuman credits as the chat line, 10 credits a minute, all-inclusive (Limits & billing). Your API secret never leaves your server or app. Pair it with an on-device avatar to give the voice a face, as the Flutter plugin and the CLI do. There are two ways in:
- WebSocket relay:
wss://api.bithuman.ai/v1/realtime, the OpenAI Realtime WebSocket protocol, unchanged. - WebRTC:
POST /v1/realtime/connectwith your SDP offer; bitHuman places the call and returns the SDP answer.
Authenticate with the api-secret header (or Authorization: Bearer <api-secret>).
WebSocket relay
wss://api.bithuman.ai/v1/realtime?model=gpt-realtime-mini
Speak the OpenAI Realtime events
exactly as you would to OpenAI: session.update, input_audio_buffer.append,
response.create, and so on. The model is fixed when you connect: a session.update that
repeats it is accepted, and one that changes it is answered with an error event whose
code is MODEL_LOCKED.
import asyncio, json, os, websockets
async def main():
async with websockets.connect(
"wss://api.bithuman.ai/v1/realtime?model=gpt-realtime-mini",
additional_headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
) as ws:
print(json.loads(await ws.recv())["type"]) # session.created
await ws.send(json.dumps({"type": "session.update", "session": {
"type": "realtime", "instructions": "Answer in one sentence.",
"output_modalities": ["audio"]}}))
print(json.loads(await ws.recv())["type"]) # session.updated
asyncio.run(main())
The Flutter plugin (2.6.20+) and the CLI (2.8.1+) connect this way for you.
WebRTC
POST /v1/realtime/connect realtimeConnect
POST /v1/realtime/connect?model=gpt-realtime-mini, with the SDP offer as the request body
(Content-Type: application/sdp). A 201 carries the SDP answer; set it as the remote
description on your peer connection. The X-Bithuman-Call response header names the call.
End the call when you are done. Billing stops the moment you hang up:
If a client only closes its peer connection without hanging up, the call ends when the connection times out, usually 8–10 seconds later, and those seconds are billed.
curl
curl -X POST https://api.bithuman.ai/v1/realtime/connect \
-H "api-secret: $BITHUMAN_API_SECRET" -H "content-type: application/json" \
-d '{"action": "hangup", "call": "<X-Bithuman-Call>"}'
Python
import os, requests
resp = requests.post(
"https://api.bithuman.ai/v1/realtime/connect",
headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
json={"action": "hangup", "call": "<X-Bithuman-Call>"},
)
print(resp.json())
Node
const resp = await fetch("https://api.bithuman.ai/v1/realtime/connect", {
method: "POST",
headers: {
"api-secret": process.env.BITHUMAN_API_SECRET,
"content-type": "application/json",
},
body: JSON.stringify({ action: "hangup", call: "<X-Bithuman-Call>" }),
});
console.log(await resp.json());
// browser: start the call from your server (it holds the api-secret), then on close:
pc.close();
await fetch("/your-server/hangup", { method: "POST", body: JSON.stringify({ call }) });
// your server: POST https://api.bithuman.ai/v1/realtime/connect
// {"action": "hangup", "call": call} with the same api-secret that started itLimits & billing
- Billing: Managed voice chat, 10 credits per minute, all-inclusive, for every active second of the session (talking or idle). A self-hosted avatar rendering the same session is included: it is not billed on top.
- Balance: a session needs a positive balance to start (
402 INSUFFICIENT_BALANCE) and is closed when the balance runs out. - Models: a standard API secret uses
gpt-realtime-mini;gpt-realtimeneeds an entitlement on your account (403 PLAN_REQUIREDotherwise; contact sales). - Length: one session lasts at most one hour.
- How a session ends on an error: an
errorevent withtype: bithuman_relaynames the code, then the socket closes with code 1008:INSUFFICIENT_BALANCE(the credits ran out),FORBIDDEN(bitHuman stopped the session) orSESSION_DURATION_LIMIT(the hour is up). Build a new session; a retry of the same one does not help. The Flutter plugin reports these codes onerrorStream(Flutter errors). PAYWALL: bitHuman Live’s refusal (HTTP 402) when an account has no credits left: its plan’s credits and its top-ups are spent. An API secret that runs out getsINSUFFICIENT_BALANCEinstead (Errors).- Other errors:
401missing or invalid key ·503the relay is at capacity (retry after theRetry-Afterseconds).
This section moved: docs.bithuman.ai/resources/renamed#older-names