Realtime mode
Realtime mode hands the whole conversation to a single speech-to-speech model instead of running the speech pipeline of recognition, AI brain and synthesis. This chapter explains how to choose between the two modes, how to set up the two realtime providers (Gemini Live and LiteBot Live), how hang-up and transfer behave, and the separate Gateway Mode for OpenAI-compatible realtime services.
Pipeline or realtime
One gateway can serve both modes at the same time. The mode is decided per call.
| Pipeline | Realtime | |
|---|---|---|
| Who listens and speaks | Your chosen speech recognition and text-to-speech providers | The model itself, one audio stream in and out |
| AI brain | LiteBot or your custom endpoint, see AI brain | Inside the model (Gemini Live) or inside LiteBot (LiteBot Live) |
| Voice | Any text-to-speech provider and voice | Only the voices the model offers |
| Tuning | Barge-in, noise floor, filler sounds, transcript gate | Handled by the model |
| Best for | Control over every stage and regional voices | Natural turn-taking and fast interruptions |
In realtime calls no speech recognition or text-to-speech provider variable applies.
How the mode is chosen
The first matching rule wins, from most specific to least.
| Order | Source | Result |
|---|---|---|
| 1 | Session metadata realtimeProvider set to gemini-live or litebot |
That realtime provider for this call |
| 1 | Session metadata realtimeProvider set to off, pipeline or an empty string |
Pipeline for this call |
| 2 | Bot configuration realtime.provider set to gemini-live |
Gemini Live |
| 2 | Bot configuration realtime.provider set to off, pipeline or empty |
Pipeline |
| 3 | REALTIME_PROVIDER |
The value of the variable; empty means pipeline |
Metadata is posted per call, see HTTP API. The bot configuration is only consulted when the metadata does not name a provider.
REALTIME_PROVIDER=
Leave it empty for pipeline by default. Set it to gemini-live or litebot to make realtime the default for every call.
Gemini Live
Gemini Live runs the bot inside the gateway. The bot's prompt, tools, model and voice come from files you control.
REALTIME_PROVIDER=gemini-live
GEMINI_LIVE_API_KEY=<your-key>
| Variable | Default | What it does |
|---|---|---|
GEMINI_LIVE_API_KEY |
falls back to GEMINI_API_KEY |
Key used for the Gemini Live connection |
GEMINI_LIVE_MODEL |
none | Model used when the bot configuration names none |
GEMINI_LIVE_VOICE |
none | Voice used when the bot configuration names none |
GEMINI_LIVE_TRANSCRIPTS |
true |
Send caller and bot transcripts to logs and webhooks |
GEMINI_LIVE_INBOUND_GAIN |
2.0 |
Gain applied to the caller's audio before it reaches the model |
GEMINI_LIVE_VAD |
empty | Empty uses the model's own speech detection; silero uses local speech detection instead |
GEMINI_LIVE_DRAIN_MS |
15000 |
Ceiling for letting the goodbye finish at hang-up |
GEMINI_LIVE_DRAIN_STALL_MS |
300 |
Abort the wait if the queued audio stops shrinking for this long |
GEMINI_LIVE_PRICE_AUDIO_IN_PER_1M |
built-in table | Audio input price in USD per million tokens |
GEMINI_LIVE_PRICE_AUDIO_OUT_PER_1M |
built-in table | Audio output price in USD per million tokens |
GEMINI_LIVE_PRICE_TEXT_IN_PER_1M |
built-in table | Text input price in USD per million tokens |
GEMINI_LIVE_PRICE_TEXT_OUT_PER_1M |
built-in table | Text output price in USD per million tokens |
The price variables override the built-in rates used for call cost; see Webhooks and billing. The bundled default bot uses the model gemini-3.1-flash-live-preview; other Gemini Live models can be set per bot.
Bots, prompts and tools
Each bot is a JSON file that points at a prompt and lists the tools it may use. Your own files go under config/ next to the gateway. Bundled bots, prompts and tools ship inside the product and are loaded first.
| Folder | Holds |
|---|---|
config/bots/*.json |
Your bot configurations |
config/prompts/*.txt |
Your prompt files |
config/tools/*.js |
Your tool modules |
BOT_REGISTRY_EXTERNAL_DIR=./config
BOT_REGISTRY_HOT_RELOAD=true
DEFAULT_BOT_ID=default
With hot reload on, edits under config/ are picked up without a restart. A bot file looks like this.
{
"id": "support",
"promptFile": "support.txt",
"realtime": {
"provider": "gemini-live",
"model": "gemini-3.1-flash-live-preview",
"voice": "Aoede",
"language": "en-US"
},
"tools": ["transferCall", "hangup"]
}
A tool module exports a name, a description, a parameters schema in JSON Schema form, and an asynchronous function that receives the parameters and the call context and returns the result. The bundled tools a bot can list are hangup and transferCall; add your own modules under config/tools/*.js and list their names.
Choosing a bot per call
Post a botId in the session metadata for the call. The id is case-sensitive; an unknown id falls back to the bot named by DEFAULT_BOT_ID.
curl -X POST http://127.0.0.1:8081/session/<call-uuid>/metadata \
-H "Content-Type: application/json" \
-d '{"botId": "support"}'
LiteBot Live
LiteBot Live makes the gateway a thin audio bridge to LiteBot's own voice endpoint. LiteBot hosts the model, the bot, the prompt, the tools, the voice, the language and the transcripts. The local bots, prompts and tools folders are ignored in this mode.
REALTIME_PROVIDER=litebot
LITEBOT_LIVE_URL=wss://<litebot-host>:<port>
LITEBOT_LIVE_API_KEY=<server-key>
LITEBOT_LIVE_BOT_ID=<bot-id>
| Variable | Default | What it does |
|---|---|---|
LITEBOT_LIVE_URL |
none, required | ws:// or wss:// base address of your LiteBot server, for example wss://host:port; the gateway adds /voice itself |
LITEBOT_LIVE_API_KEY |
none | LiteBot server key used to authenticate |
LITEBOT_LIVE_TOKEN |
none | A JWT, as an alternative to the server key |
LITEBOT_LIVE_BOT_ID |
falls back to LITEBOT_BOT_ID |
Bot to start on LiteBot |
LITEBOT_LIVE_INBOUND_RATE |
16000 |
Sample rate of caller audio; use 8000 if the bot is set up for 8 kHz |
LITEBOT_LIVE_INBOUND_GAIN |
falls back to GEMINI_LIVE_INBOUND_GAIN |
Gain applied to the caller's audio |
LITEBOT_LIVE_KICKOFF |
true |
Ask the bot to greet first once the session is ready |
LITEBOT_LIVE_PING_MS |
30000 |
Keep-alive interval; 0 disables it |
LITEBOT_LIVE_CONTINUOUS_STREAM |
on | Keep a steady audio stream flowing to LiteBot even when the handset sends nothing during silence |
LITEBOT_LIVE_DRAIN_MS |
15000 |
Ceiling for letting the goodbye finish when the session ends |
LITEBOT_LIVE_DRAIN_STALL_MS |
300 |
Abort the wait if the queued audio stops shrinking for this long |
LITEBOT_LIVE_TRANSFER_TOOLS |
escalateToHuman |
Comma-separated tool names whose execution triggers a transfer |
Leave LITEBOT_LIVE_CONTINUOUS_STREAM on. Some handsets stop sending audio during silence, and without a steady stream the model never decides the caller's turn has ended.
The gateway passes session details to LiteBot as variables so the bot can use them: caller name, caller number, channel and call direction, plus anything else you posted in the session metadata.
Hang-up and transfer in realtime mode
Both providers handle the end of a call the same way. When the bot decides to hang up, the gateway lets the goodbye finish playing and then closes the call. The wait is bounded by the drain ceiling (GEMINI_LIVE_DRAIN_MS or LITEBOT_LIVE_DRAIN_MS), and it ends early if the queued audio stops shrinking for the stall time. A hard safety net closes the call shortly after the ceiling in case anything is stuck.
| Provider | How a transfer starts |
|---|---|
| Gemini Live | The bot calls the transferCall tool |
| LiteBot Live | The tool result carries a transfer instruction, or the tool is named in LITEBOT_LIVE_TRANSFER_TOOLS |
Where the call is sent is configured as for pipeline calls: TRANSFER_SIP_URI in SIP trunk for SIP-bridge calls, TRANSFER_ENDPOINT in Asterisk and AudioSocket for AudioSocket calls, and the caller-facing behaviour in Conversation behaviour.
Gateway Mode
Gateway Mode is a separate alternative to REALTIME_PROVIDER. It bridges an AudioSocket call straight to any WebSocket service that speaks the OpenAI Realtime protocol. There are no bots, tools or transfers in this mode; the remote service does everything.
| Variable | Default | What it does |
|---|---|---|
GATEWAY_MODE |
false |
true bridges every call to the remote service |
LLM_WEBSOCKET_URL |
none, required | WebSocket address of the realtime service |
LLM_API_KEY |
none | Key sent to the service |
GATEWAY_INSTRUCTIONS |
You are a helpful AI assistant. Be concise and natural. |
System instructions for the session |
GATEWAY_VOICE |
alloy |
Voice requested from the service |
GATEWAY_MODALITIES |
audio,text |
Output types requested |
GATEWAY_TEMPERATURE |
0.8 |
Sampling temperature |
GATEWAY_MAX_TOKENS |
4096 |
Maximum tokens per response |
GATEWAY_VAD_TYPE |
server_vad |
Speech detection type |
GATEWAY_VAD_THRESHOLD |
0.5 |
Speech detection sensitivity |
GATEWAY_VAD_SILENCE_MS |
500 |
Silence that ends a turn |
GATEWAY_VAD_PREFIX_MS |
300 |
Audio kept from before speech starts |
GATEWAY_ENABLE_TRANSCRIPTION |
off | Ask the service to transcribe the caller |
GATEWAY_TRANSCRIPTION_MODEL |
whisper-1 |
Model used for that transcription |
GATEWAY_LOG_TRANSCRIPTS |
off | Write transcripts to the log |
GATEWAY_COMMIT_ON_SILENCE |
off | Commit the audio buffer when silence is detected |
GATEWAY_MAX_ERRORS |
10 |
Errors tolerated before the call is dropped |