Exotel Integration

Configure Exotel for voice agent calls in India with WebSocket streaming and latency optimization.

2 min read

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#

  1. Exotel account with Voice Streaming enabled
  2. API credentials (SID, API Key, API Token)
  3. A virtual phone number (ExoPhone)
  4. 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:

  1. Go to App Bazaar → My Apps
  2. Create or edit your Voicebot App
  3. 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 the extra HTTP redirect round trip.

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
                ↓
         extra latency (ringing before audio)

Optimized Static URL Flow#

Exotel → Direct WebSocket → Extract call_sid → Lookup agent
                ↓
         No HTTP redirect latency!

Implementation:

  1. Configure static WebSocket URL in Exotel Applet:

    wss://your-domain.com/exotel/stream
    
  2. Store call details with call_sid during outbound call:

    // In ExotelMakeCall(), after getting callSid:
    RedisClient.Set(ctx, "exotel:callsid:"+callSid, callDetails, 1*time.Hour)
    
  3. 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#

  1. Verify ExoPhone is active
  2. Check API credentials
  3. Ensure Voicebot App is configured correctly
  4. 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#

  1. Ensure Linear PCM encoding (not μ-law)
  2. Verify sample rate is 8000 Hz
  3. 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#

Was this page helpful?