API Endpoints
Voice Call API
Twilio voice integration with OpenAI Realtime API
GET
Voice Call API
Voice Call API
The Voice Call API provides Twilio voice integration with OpenAI’s Realtime API for voice-based AI interactions.Binding
praisonai call binds to 127.0.0.1:8090 by default. Inside a container or VM, use --host 0.0.0.0 so the server is reachable from outside the container boundary — see Binding & Network Access for details. Always pair an exposed port with CALL_SERVER_TOKEN.
Base URL
Endpoints
Status Page
Check if the server is running./status
Returns HTML status page
Incoming Call Handler
Handle incoming Twilio calls and return TwiML response./
Twilio webhook for incoming calls
Media Stream WebSocket
WebSocket endpoint for real-time audio streaming between Twilio and OpenAI./media-stream
WebSocket for audio streaming
- Twilio connects to WebSocket
- Server connects to OpenAI Realtime API
- Audio streams bidirectionally:
- Twilio → Server → OpenAI (user speech)
- OpenAI → Server → Twilio (AI response)
OpenAI Events
Configuration
Environment Variables
Realtime Timeouts
The realtime WebSocket runs with bounded connect / heartbeat / close so a dead upstream cannot hold a Twilio media leg indefinitely:open_timeout=10, ping_interval=20, ping_timeout=20, close_timeout=5, max_size=1 MiB. These are hardcoded, not env-tunable. See Point the realtime endpoint elsewhere for the override flow.
Rate Limiting & Multi-Tenant Isolation
Eachbuild_call_app() tenant enforces per-IP request rate limiting and a total concurrent-connection cap. As of PR #5348, both counters live on that app’s CallAppState — co-hosted tenants in a single process no longer cross-consume each other’s connection budget.
Rate Limiting
Bounded IP tracking
The per-IP request-timestamp map is an LRU-bounded
OrderedDict (default cap 10_000 IPs, configurable via the max_tracked_ips argument to CallAppState(...)):
- Expired timestamps are pruned eagerly on read (
touch_ip), and an IP whose window has emptied is dropped from the map immediately — a rejected caller leaves no residue. - On admit (
record_ip), the IP is pinned MRU and the oldest entries are evicted so the map never exceedsmax_tracked_ips. Provision above your realistic distinct-IP working set (the 10k default is generous) so eviction only bites under a rotating-IP flood — exactly the unbounded-growth case the bound exists to contain.
client_ips was a plain module-global defaultdict(list) — a caller rotating source IPs could grow it without bound.
Custom max_tracked_ips:
Custom Tools
Create atools.py file in the working directory:
Usage Example
Start Server
Twilio Configuration
- Get your public URL (ngrok or deployed)
- In Twilio Console:
- Go to Phone Numbers → Your Number
- Set Voice webhook to:
https://your-url.ngrok.io/ - Method: POST
Python Usage
Session Configuration
The server sends this session configuration to OpenAI:Related
- Point the realtime endpoint elsewhere - Azure / self-hosted realtime overrides
- CLI - Command-line interface
- Agent - Agent configuration

