Loading Cookbooks...
Loading Cookbooks...
This guide extends the basic Twilio + OrbitalsAI setup with two things: a language selection menu that plays when the caller dials in, and a browser dashboard that shows live transcripts without you having to watch a terminal.
If you haven't completed the basic guide yet, start here. This one assumes you already have a working server, a configured Twilio number, and ngrok running.
When someone calls your number, before any audio is streamed, they hear:
"For English, press 1. For Hausa, press 2. For Igbo, press 3. For Yoruba, press 4."
That's a standard DTMF gather. This is the same mechanism MTN, Airtel, and most African telcos use for multilingual IVR menus. The digit the caller presses gets passed into the streaming session so OrbitalsAI transcribes in that language from the first word.
The browser dashboard polls a /transcripts endpoint every 3 seconds and renders the latest transcript per active call. It's a simple polling approach, good enough to monitor a running call without touching the terminal.
The key step is what happens at /language: the caller's digit is mapped to a language code and embedded as a <Parameter> in the TwiML <Stream>. When the WebSocket opens, your server reads that parameter and passes the language to OrbitalsAI before any audio arrives.
Every endpoint configuration in these recipes uses local credentials. Replace placeholders (like YOUR_API_KEY) with your actual API key retrieved from the OrbitalsAI Dashboard.
The key step is what happens at /language: the caller's digit is mapped to a language code and embedded as a <Parameter> in the TwiML <Stream>. When the WebSocket opens, your server reads that parameter and passes the language to OrbitalsAI before any audio arrives.
It helps to see the extra step before touching the code:
Caller dials Twilio number
↓
POST /: Twilio webhook (TwiML menu)
↓
Caller presses digit
↓
POST /language: Twilio posts digit + CallSid
↓
WebSocket /realtime: reads lang + call_id, starts transcriber
↓
OrbitalsAI Streaming API
↓
Live browser dashboard (polls /transcripts every 3s)
The key step is what happens at /language: the caller's digit is mapped to a language code and embedded as a <Parameter> in the TwiML <Stream>. When the WebSocket opens, your server reads that parameter and passes the language to OrbitalsAI before any audio arrives.
Three things change from the basic version:
1. A module-level RECENT_TRANSCRIPTS deque that's shared across all active calls and imported by main.py.
2. TwilioOrbitalsHandlers now takes language, call_id, and store, and writes every transcript into the store.
3. TwilioOrbitalsTranscriber takes a language argument, and connect() calls self._client.configure() explicitly after connecting.
That last point is the one that will cost you an hour if you miss it: `StreamingConfig` alone does not transmit the language over the wire. You must call configure() after connect() or every call transcribes in English regardless of what the caller selected.
The server now has four routes:
- GET /: Browser dashboard
- POST /: Twilio webhook (returns the <Gather> IVR prompt)
- POST /language: Receives the digit, starts <Stream> with language parameter
- GET /transcripts: Returns transcript deque as JSON
- WebSocket /realtime: Receives Twilio audio, drives the transcriber
Open http://localhost:5000 in a browser. You'll see the dashboard with an empty transcript list (that's correct, nothing has called in yet.
Your ngrok and Twilio configuration from the basic guide still apply. No changes needed there.
When you initiate a connection and make a phone call, your server console or browser display will output values formatted as follows:
Add more languages without touching the server. Extend language_map in /language and add a line to the <Say> prompt. OrbitalsAI supports Swahili (swahili), Kinyarwanda (kinyarwanda), and Nigerian Pidgin (pidgin), among others. Check the supported languages page for the full list.
Persist transcripts across restarts. The deque is in-memory only. It's gone when the server stops. For production, replace the self.store.append() call in TwilioOrbitalsHandlers._push() with a write to PostgreSQL, SQLite, or Firestore, keyed by call_id.
Replace polling with a WebSocket push. The 3-second poll adds up to 3 seconds of display lag. For a truly live dashboard, add a second WebSocket endpoint (/ws/transcripts) and push new entries to the browser as they arrive. The polling approach here is simpler to deploy and a reasonable starting point.
Multiple simultaneous calls work already. Each WebSocket connection creates its own TwilioOrbitalsTranscriber instance. The shared RECENT_TRANSCRIPTS deque is thread-safe. No changes needed.