- Your server calls
POST /v1/realtime/sessionswith your API key and gets aclient_secret. - Your client opens
wss://api.voice.wixzel.com/v1/realtime?client_secret=…and streams audio.
1. Create a session (server)
Response
engine in the response is the agent’s voice engine: classic, sarvam,
gemini_live, deepgram_agent, openai_live, openai_realtime or
grok_voice. All seven work over realtime.
Needs the calls:write scope. Nothing is charged here: a user who opens
your page and then denies the microphone costs you nothing.
2. Connect (client)
In a browser
The TypeScript SDK ships a browser client that handles the microphone, encoding, playback and barge-in:https://, or localhost) and asks for the
microphone when connect is called. To ask before you mint a session,
call requestMicrophone() first and pass the stream to session.start().
In Flutter or Dart
The Dart SDK has the protocol client. Microphone and speaker access are your app’s: use any recording plugin that gives you 16-bit PCM at 8 kHz, and any PCM player:Turn on your platform’s echo cancellation. Without it the agent hears itself
through the speaker and interrupts itself.
With the agent’s mascot
Every agent has a mascot, an animated face, and it can lip-sync to a session: it thinks while the session connects, listens while the user talks, and moves its mouth in time with the agent’s audio. In a browser,mountMascot from wixzel-voice/mascot draws it and face.connect(session)
opens the session with the mascot wired in. In Flutter, AgentMascotView from
wixzel_voice_flutter draws it and MascotRealtimeLink follows a
RealtimeConnection. See Draw it in your own app
and SDKs: Mascot widgets.
Anything else
The protocol is plain JSON over a WebSocket, below.The protocol
Every frame is a JSON text message with atype. Audio is base64 G.711
µ-law, 8 kHz, mono in both directions, exactly what a phone line carries.
Send it in 20 ms frames (160 bytes).
From the server
From the client
While the agent is saying its opening line, keep sending audio but send
silence (
0xFF bytes): the agent then cannot be interrupted by room noise
before the user has heard anything. The browser client does this for you.
Why a session ends
Refusals
A browser cannot read the HTTP status of a refused WebSocket, so refusals arrive as anerror frame followed by a close with one of these codes:
Billing and records
A realtime session is a call to everything but the phone network:- It is billed at the same per-minute price as a phone call on the same engine, per second, from credit. See pricing.
- It counts toward your concurrent-call limit (5 by default) and the API key’s spend limit.
- It appears in
GET /v1/callswith"channel": "web"(filter with?channel=web), inbound, with nofromorto, and with its transcript. - It ends with a
callCompletedwebhook carrying"provider": "web"and yourmetadata.

