Skip to main content
A phone call is one way to reach an agent. Realtime is the other: your user talks to the same agent from a browser tab, a mobile app or a kiosk, over a WebSocket. The engines, prompts, knowledge bases and appointment booking are the ones your phone calls use; only the line is different. Two steps, and the split is the security model:
  1. Your server calls POST /v1/realtime/sessions with your API key and gets a client_secret.
  2. Your client opens wss://api.voice.wixzel.com/v1/realtime?client_secret=… and streams audio.
The secret works once, for one minute, for one agent. Your API key never leaves your server.

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:
It needs a secure page (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 a type. 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 an error 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/calls with "channel": "web" (filter with ?channel=web), inbound, with no from or to, and with its transcript.
  • It ends with a callCompleted webhook carrying "provider": "web" and your metadata.
Human transfer is not available: there is no second line to hand the user to. Web sessions are not recorded.