wss://api.meshapi.ai/v1/realtime.
Most issues are connection, auth, or event-shape problems. See
Realtime Audio for the full reference.
Can’t connect over HTTP
This endpoint exists only as a WebSocket upgrade. A plain HTTP request to/v1/realtime is not served. Connect with a WebSocket client. Also, TLS is
mandatory: ws:// is rejected, use wss://.
Auth fails, especially in the browser
Two methods: theSec-WebSocket-Protocol: openai-realtime, Bearer <rsk_...>
subprotocol (preferred), or ?api_key=<rsk_...> on the URL.
- Browsers can’t set request headers on a WebSocket and can’t reliably send the space-separated
Bearersubprotocol — use the?api_key=query fallback. - If both are present, the subprotocol wins.
- An
Authorizationheader is not read on this endpoint. A client that only sets one (e.g.websocat -H "Authorization: ...") getsinvalid_api_key.
session.update is rejected
Realtime uses OpenAI’s GA event protocol. Use the GA shape ({"type":"realtime","output_modalities":[...],"audio":{...}}). The older
beta shape (modalities / voice / input_audio_format) is rejected.
Audio isn’t received
Audio frames are base64 inside text events (input_audio_buffer.append up,
response.output_audio.delta down), as OpenAI’s protocol defines. Mesh forwards
binary WebSocket frames unchanged, but the protocol doesn’t read raw PCM sent
that way. Send audio as input_audio_buffer.append events.
Session drops after a while
- 30-minute cap. Mesh closes the session with
session_duration_exceeded. Reconnect and resume app-level state for long agents. - 60s idle timeout. Mesh closes a socket with no client→server frame for 60s, with
idle_timeout. Keep the audio buffer flowing or send asession.updateping. service_draining/connection_timeout. The server restarted or the heartbeat was lost. Reconnect.
Reading errors — use error.code, not the close code
Errors arrive as a JSON frame ({ "type": "error", "error": { "code", "message" }, "request_id" }) just
before the socket closes. Branch on error.code — the close code is incidental.
More codes, including the lifecycle ones, are in the
error code reference. Close codes seen:
1000 (normal closure, including
auth, quota and limit errors) and 1011 (server/upstream conditions).
Session won’t open / billing surprises
- Opening a session holds up to $10 of your balance until it closes; if your balance can’t cover the hold, you get
insufficient_quota. - Sessions cut short (network drop, tab close) still bill for tokens already processed.
- There’s no in-stream cost message. Usage is settled every 60 seconds and at close. Query
GET /v1/usagefor canonical numbers.