Exotel Integration
Exotel is a leading cloud telephony provider in India, offering reliable voice services with excellent coverage across the country.
Why Exotel?
- Indian Coverage: Best-in-class coverage for India
- Regulatory Compliance: TRAI compliant, handles DND automatically
- Local Numbers: Indian virtual numbers (+91)
- Cost Effective: Competitive per-minute rates for Indian calls
Prerequisites
- Exotel account with Voice Streaming enabled
- API credentials (SID, API Key, API Token)
- A virtual phone number (ExoPhone)
- Voicebot App ID configured
Configuration
Environment Variables
EXOTEL_SID=your_exotel_sid
EXOTEL_API_KEY=your_api_key
EXOTEL_API_TOKEN=your_api_token
EXOTEL_PHONE_NUMBER=your_exophone
EXOTEL_APP_ID=your_voicebot_app_id
EXOTEL_SUB_DOMAIN=api.exotel.com # or your regional subdomain
Exotel App Configuration
In your Exotel dashboard, configure the Voicebot Applet:
- Go to App Bazaar → My Apps
- Create or edit your Voicebot App
- Set the WebSocket URL:
Option A: Dynamic URL (Current)
https://your-domain.com/ws_connect
This endpoint returns the WebSocket URL dynamically.
Option B: Static URL (Recommended for lower latency)
wss://your-domain.com/exotel/stream
Eliminates HTTP redirect latency (~100-500ms).
Call Flows
Outbound Call Flow
1. POST /make-call with agent_id, phone_number
↓
2. ExotelMakeCall() → Exotel API (get CallSid)
↓
3. Exotel rings recipient
↓
4. Recipient answers
↓
5. [Dynamic] GET /ws_connect → Returns WebSocket URL
[Static] Direct WebSocket connection
↓
6. WebSocket streams audio bidirectionally
↓
7. Voice agent conversation
↓
8. Call ends → Webhook with recording URL
Inbound Call Flow
1. Caller dials your ExoPhone
↓
2. Exotel triggers Voicebot App
↓
3. GET /ws_connect?Direction=incoming&From=caller_number
↓
4. Returns WebSocket URL with agent routing
↓
5. WebSocket connection established
↓
6. Voice agent handles call
API Reference
Make Outbound Call
POST /make-call
Content-Type: application/json
{
"agent_id": "123",
"phone_number": "9876543210",
"workspace_id": "ws_456",
"variables": {
"customer_name": "John",
"order_id": "ORD-789"
}
}
Response:
{
"status": "success",
"call_sid": "exotel_call_sid_here"
}
WebSocket Message Format
Exotel uses snake_case for message fields:
Start Message (from Exotel):
{
"event": "start",
"stream_sid": "stream_abc123",
"start": {
"call_sid": "call_xyz789",
"account_sid": "your_account_sid",
"from": "09876543210",
"to": "your_exophone"
}
}
Media Message (from Exotel):
{
"event": "media",
"media": {
"payload": "base64_encoded_audio..."
}
}
Audio Response (to Exotel):
{
"event": "media",
"stream_sid": "stream_abc123",
"media": {
"payload": "base64_encoded_audio..."
}
}
Clear Playback:
{
"event": "clear",
"stream_sid": "stream_abc123"
}
Audio Specifications
| Parameter | Value |
|---|---|
| Sample Rate | 8000 Hz |
| Channels | Mono |
| Encoding | Linear PCM 16-bit |
| Chunk Size | ~3.2 KB (base64) |
| Chunk Duration | ~100ms |
Latency Optimization
Current Dynamic URL Flow
Exotel → HTTP /ws_connect → Parse → Return URL → WebSocket
↓
+100-500ms latency (ringing before audio)
Optimized Static URL Flow
Exotel → Direct WebSocket → Extract call_sid → Lookup agent
↓
No HTTP redirect latency!
Implementation:
-
Configure static WebSocket URL in Exotel Applet:
wss://your-domain.com/exotel/stream -
Store call details with
call_sidduring outbound call:// In ExotelMakeCall(), after getting callSid: RedisClient.Set(ctx, "exotel:callsid:"+callSid, callDetails, 1*time.Hour) -
Lookup agent when WebSocket connects:
// In WebSocket handler, after receiving "start" message: callSid := startMsg["start"]["call_sid"] callDetails := RedisClient.Get(ctx, "exotel:callsid:"+callSid)
Handling DND (Do Not Disturb)
Exotel automatically handles TRAI NDNC (National Do Not Call) registry:
if resp.StatusCode == 403 {
if strings.Contains(response.RestException.Message, "TRAI NDNC regulations") {
return "dnd", "" // Handle DND gracefully
}
}
Status Callbacks
Configure webhook for call status updates:
POST /api/webhooks/telephony/exotel
Exotel sends:
{
"CallSid": "call_sid",
"Status": "completed",
"Duration": "120",
"RecordingUrl": "https://...",
"CustomField": "pin=123&user_id_pin=xyz"
}
Troubleshooting
Call Not Connecting
- Verify ExoPhone is active
- Check API credentials
- Ensure Voicebot App is configured correctly
- Verify WebSocket URL is publicly accessible
High Latency Before First Audio
- Switch to static WebSocket URL configuration
- Check network latency to Exotel servers
Audio Quality Issues
- Ensure Linear PCM encoding (not μ-law)
- Verify sample rate is 8000 Hz
- Check for packet loss in WebSocket connection
Cost Optimization
| Call Type | Approximate Cost |
|---|---|
| Outbound (Local) | ₹0.50-1.00/min |
| Outbound (National) | ₹0.80-1.50/min |
| Inbound | ₹0.30-0.50/min |
Prices vary by plan and volume. Contact Exotel for exact pricing.
Next Steps
- Twilio Integration - Alternative telephony provider
- Deepgram STT - Optimize speech recognition
- Latency Optimization - Further reduce response time