# Exotel Integration

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

Source: https://edesy.in/docs/voice-agent/telephony/exotel

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

```bash
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 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

```bash
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:**
```json
{
  "status": "success",
  "call_sid": "exotel_call_sid_here"
}
```

### WebSocket Message Format

Exotel uses **snake_case** for message fields:

**Start Message (from Exotel):**
```json
{
  "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):**
```json
{
  "event": "media",
  "media": {
    "payload": "base64_encoded_audio..."
  }
}
```

**Audio Response (to Exotel):**
```json
{
  "event": "media",
  "stream_sid": "stream_abc123",
  "media": {
    "payload": "base64_encoded_audio..."
  }
}
```

**Clear Playback:**
```json
{
  "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:**

1. Configure static WebSocket URL in Exotel Applet:
   ```
   wss://your-domain.com/exotel/stream
   ```

2. Store call details with `call_sid` during outbound call:
   ```go
   // In ExotelMakeCall(), after getting callSid:
   RedisClient.Set(ctx, "exotel:callsid:"+callSid, callDetails, 1*time.Hour)
   ```

3. Lookup agent when WebSocket connects:
   ```go
   // 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:

```go
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:

```bash
POST /api/webhooks/telephony/exotel
```

Exotel sends:
```json
{
  "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

- [Twilio Integration](https://edesy.in/docs/voice-agent/telephony/twilio) - Alternative telephony provider
- [Deepgram STT](https://edesy.in/docs/voice-agent/providers/deepgram) - Optimize speech recognition
- [Latency Optimization](https://edesy.in/docs/voice-agent/advanced/latency) - Further reduce response time
