|
| 1 | +--- |
| 2 | +title: "Exotel WebSocket Integration" |
| 3 | +sidebarTitle: "Exotel WebSocket" |
| 4 | +description: "Complete guide to using Exotel Voice Streaming with Pipecat for dial-in and dial-out functionality" |
| 5 | +--- |
| 6 | + |
| 7 | +## Things you'll need |
| 8 | + |
| 9 | +- An active [Exotel](https://exotel.com) account with voice streaming enabled |
| 10 | +- One or more Exotel provisioned phone numbers |
| 11 | +- A public-facing server or tunneling service like [ngrok](https://ngrok.com/) (for dial-out only) |
| 12 | +- API keys for speech-to-text, text-to-speech, and LLM services |
| 13 | + |
| 14 | +<CardGroup cols={2}> |
| 15 | + <Card |
| 16 | + title="Exotel Dial-in Example" |
| 17 | + icon="phone-arrow-down-left" |
| 18 | + href="https://github.com/pipecat-ai/pipecat-examples/tree/main/exotel-chatbot/inbound" |
| 19 | + > |
| 20 | + Complete dial-in implementation using Exotel Voice Streaming over WebSocket |
| 21 | + </Card> |
| 22 | + |
| 23 | + <Card |
| 24 | + title="Exotel Dial-out Example" |
| 25 | + icon="phone-arrow-up-right" |
| 26 | + href="https://github.com/pipecat-ai/pipecat-examples/tree/main/exotel-chatbot/outbound" |
| 27 | + > |
| 28 | + Outbound calling using Exotel Voice Streaming with WebSocket transport |
| 29 | + </Card> |
| 30 | +</CardGroup> |
| 31 | + |
| 32 | +## Phone Number Setup |
| 33 | + |
| 34 | +You'll need Exotel phone numbers for both dial-in and dial-out functionality: |
| 35 | + |
| 36 | +- Visit [exotel.com](https://exotel.com) and purchase phone numbers |
| 37 | +- Complete KYC verification if required |
| 38 | +- Ensure voice streaming is enabled on your account (contact Exotel support if needed) |
| 39 | + |
| 40 | +## Environment Setup |
| 41 | + |
| 42 | +Configure your environment variables for Exotel and AI services: |
| 43 | + |
| 44 | +```shell .env |
| 45 | +EXOTEL_ACCOUNT_SID=... |
| 46 | +EXOTEL_API_KEY=... |
| 47 | +EXOTEL_API_TOKEN=... |
| 48 | +OPENAI_API_KEY=... |
| 49 | +DEEPGRAM_API_KEY=... |
| 50 | +CARTESIA_API_KEY=... |
| 51 | +``` |
| 52 | + |
| 53 | +## Dial-in |
| 54 | + |
| 55 | +Dial-in allows users to call your Exotel number and connect to your Pipecat bot via WebSocket Voice Streaming. Unlike other providers, Exotel automatically provides caller information (to/from numbers) in the WebSocket messages, so no custom server is needed for basic dial-in functionality. |
| 56 | + |
| 57 | +### How It Works |
| 58 | + |
| 59 | +Here's the sequence of events when someone calls your Exotel number: |
| 60 | + |
| 61 | +1. **Exotel receives an incoming call** to your phone number |
| 62 | +2. **Exotel executes your App Bazaar flow** which includes a Voicebot applet |
| 63 | +3. **Exotel opens a WebSocket** to your server with real-time audio and call metadata |
| 64 | +4. **Your bot processes the audio** using the Pipecat pipeline |
| 65 | +5. **The bot responds with audio** sent back to Exotel over WebSocket |
| 66 | +6. **Exotel plays the audio** to the caller in real-time |
| 67 | + |
| 68 | +### Set up your App Bazaar flow |
| 69 | + |
| 70 | +Exotel uses App Bazaar to configure call flows. For dial-in, you create an app with a Voicebot applet that establishes a WebSocket connection directly to your bot: |
| 71 | + |
| 72 | +1. **Navigate to App Bazaar** in your Exotel dashboard |
| 73 | +2. **Create a new app** or edit an existing one |
| 74 | +3. **Add a Voicebot applet** (not "Stream" or "Passthru") |
| 75 | +4. **Configure the WebSocket URL**: `wss://your-server.com/ws` |
| 76 | +5. **Add a Hangup applet** at the end to properly terminate calls |
| 77 | + |
| 78 | +Your flow should look like: `Call Start → [Voicebot Applet] → [Hangup Applet]` |
| 79 | + |
| 80 | +### Custom Data Limitations |
| 81 | + |
| 82 | +<Warning> |
| 83 | + Currently, there are limitations with passing custom data through query |
| 84 | + parameters in Exotel's WebSocket URLs. While the platform theoretically |
| 85 | + supports query parameters, they may be stripped during call processing. This |
| 86 | + is an ongoing limitation that may be addressed in future Exotel updates. |
| 87 | +</Warning> |
| 88 | + |
| 89 | +If you need to pass custom data, consider these alternatives: |
| 90 | + |
| 91 | +- Use different WebSocket endpoints for different call types |
| 92 | +- Configure multiple App Bazaar flows for different scenarios |
| 93 | +- Handle customization based on the called number (available in WebSocket messages) |
| 94 | + |
| 95 | +### Configure your Pipecat bot for dial-in |
| 96 | + |
| 97 | +Your bot receives the WebSocket connection and automatically gets call information from Exotel's WebSocket messages. The key components are: |
| 98 | + |
| 99 | +**WebSocket Parsing**: Pipecat's built-in parser extracts call data from Exotel's WebSocket messages: |
| 100 | + |
| 101 | +```python bot.py |
| 102 | +from pipecat.runner.utils import parse_telephony_websocket |
| 103 | + |
| 104 | +async def run_bot(websocket: WebSocket): |
| 105 | + # Parse Exotel WebSocket data |
| 106 | + transport_type, call_data = await parse_telephony_websocket(websocket) |
| 107 | + |
| 108 | + # Extract call information (automatically provided by Exotel) |
| 109 | + stream_id = call_data["stream_id"] |
| 110 | + call_id = call_data["call_id"] # Exotel's CallSid |
| 111 | + account_sid = call_data["account_sid"] |
| 112 | + from_number = call_data["from"] # Caller's number |
| 113 | + to_number = call_data["to"] # Your Exotel number |
| 114 | +``` |
| 115 | + |
| 116 | +**Transport Creation**: Configure the WebSocket transport with Exotel serialization: |
| 117 | + |
| 118 | +```python bot.py |
| 119 | +# Create Exotel serializer with call details |
| 120 | +serializer = ExotelFrameSerializer( |
| 121 | + stream_id=stream_id, |
| 122 | + call_id=call_id, |
| 123 | + account_sid=account_sid, |
| 124 | + api_key=os.getenv("EXOTEL_API_KEY"), |
| 125 | + api_token=os.getenv("EXOTEL_API_TOKEN"), |
| 126 | +) |
| 127 | + |
| 128 | +# Configure WebSocket transport |
| 129 | +transport = FastAPIWebsocketTransport( |
| 130 | + websocket=websocket, |
| 131 | + params=FastAPIWebsocketParams( |
| 132 | + audio_in_enabled=True, |
| 133 | + audio_out_enabled=True, |
| 134 | + add_wav_header=False, |
| 135 | + vad_analyzer=SileroVADAnalyzer(), |
| 136 | + serializer=serializer, |
| 137 | + ), |
| 138 | +) |
| 139 | + |
| 140 | +# Your bot pipeline setup here... |
| 141 | +``` |
| 142 | + |
| 143 | +**Call Information Usage**: Use the extracted call information to personalize your bot's behavior: |
| 144 | + |
| 145 | +```python bot.py |
| 146 | +# Customize bot behavior based on call information |
| 147 | +greeting = f"Hello! I see you're calling from {from_number}. How can I help you today?" |
| 148 | + |
| 149 | +# Use call information for logging or routing |
| 150 | +logger.info(f"Incoming call from {from_number} to {to_number}") |
| 151 | +``` |
| 152 | + |
| 153 | +<Card |
| 154 | + title="Complete Bot Implementation" |
| 155 | + icon="code" |
| 156 | + href="https://github.com/pipecat-ai/pipecat-examples/blob/main/exotel-chatbot/inbound/bot.py" |
| 157 | +> |
| 158 | + See the full bot.py with WebSocket parsing, transport setup, and pipeline |
| 159 | + configuration |
| 160 | +</Card> |
| 161 | + |
| 162 | +### Set up Exotel phone number |
| 163 | + |
| 164 | +Configure your Exotel phone number to use your App Bazaar flow: |
| 165 | + |
| 166 | +1. Go to the [Exotel Dashboard](https://my.exotel.com) |
| 167 | +2. Navigate to ExoPhones |
| 168 | +3. Find your phone number and click the edit icon |
| 169 | +4. Under "App", select your App Bazaar flow |
| 170 | +5. Save the configuration |
| 171 | + |
| 172 | +### Run the Example |
| 173 | + |
| 174 | +For dial-in, you can run your bot directly without a separate server: |
| 175 | + |
| 176 | +```shell |
| 177 | +# Start your bot directly |
| 178 | +python bot.py |
| 179 | + |
| 180 | +# For local development, use ngrok to expose your WebSocket |
| 181 | +ngrok http 8000 |
| 182 | +# Use the ngrok WebSocket URL (wss://abc123.ngrok.io/ws) in your App Bazaar flow |
| 183 | +``` |
| 184 | + |
| 185 | +<Card |
| 186 | + title="Complete Setup Instructions" |
| 187 | + icon="book-open" |
| 188 | + href="https://github.com/pipecat-ai/pipecat-examples/tree/main/exotel-chatbot/inbound" |
| 189 | +> |
| 190 | + See the full README with step-by-step setup, App Bazaar configuration, and |
| 191 | + testing |
| 192 | +</Card> |
| 193 | + |
| 194 | +## Dial-out |
| 195 | + |
| 196 | +Dial-out allows your bot to initiate calls to phone numbers using Exotel's outbound calling capabilities with WebSocket Voice Streaming. |
| 197 | + |
| 198 | +### How It Works |
| 199 | + |
| 200 | +Here's the sequence of events for dial-out calls: |
| 201 | + |
| 202 | +1. **Your application triggers a dial-out** (via API call or user action) |
| 203 | +2. **Server initiates an Exotel call** using the Voice API |
| 204 | +3. **Exotel establishes the call** and opens a WebSocket connection |
| 205 | +4. **Your bot joins the WebSocket** and sets up the pipeline |
| 206 | +5. **The recipient answers** and is connected to your bot |
| 207 | +6. **The bot handles the conversation** with real-time audio streaming |
| 208 | + |
| 209 | +### Set up your server for dial-out |
| 210 | + |
| 211 | +The dial-out server creates outbound calls and manages WebSocket connections. When you trigger a dial-out call, your server: |
| 212 | + |
| 213 | +1. **Receives the dial-out request** with target phone number and parameters |
| 214 | +2. **Creates an Exotel call** using the Voice API with a callback URL |
| 215 | +3. **Accepts the WebSocket** connection from Exotel |
| 216 | +4. **Parses call data** from the WebSocket messages |
| 217 | +5. **Runs the Pipecat bot** with the call information |
| 218 | + |
| 219 | +<Card |
| 220 | + title="Complete Server Implementation" |
| 221 | + icon="code" |
| 222 | + href="https://github.com/pipecat-ai/pipecat-examples/blob/main/exotel-chatbot/outbound/server.py" |
| 223 | +> |
| 224 | + See the full FastAPI server code with outbound call creation and WebSocket |
| 225 | + handling |
| 226 | +</Card> |
| 227 | + |
| 228 | +### Configure your Pipecat bot for dial-out |
| 229 | + |
| 230 | +The dial-out bot configuration is similar to dial-in, with Exotel automatically providing call information. Note the [Custom Data Limitations](#custom-data-limitations) section above. |
| 231 | + |
| 232 | +**Call Information**: Extract call details from Exotel's WebSocket messages: |
| 233 | + |
| 234 | +```python bot.py |
| 235 | +# Parse WebSocket data (same as dial-in) |
| 236 | +transport_type, call_data = await parse_telephony_websocket(websocket) |
| 237 | + |
| 238 | +# Extract call information |
| 239 | +stream_id = call_data["stream_id"] |
| 240 | +call_id = call_data["call_id"] |
| 241 | +account_sid = call_data["account_sid"] |
| 242 | +from_number = call_data["from"] # Your Exotel number |
| 243 | +to_number = call_data["to"] # Target number you're calling |
| 244 | + |
| 245 | +# Customize bot behavior for outbound calls |
| 246 | +greeting = f"Hi! This is an automated call from {from_number}. How are you today?" |
| 247 | +``` |
| 248 | + |
| 249 | +**Transport Configuration**: Same transport setup as dial-in: |
| 250 | + |
| 251 | +```python bot.py |
| 252 | +# Create Exotel serializer |
| 253 | +serializer = ExotelFrameSerializer( |
| 254 | + stream_id=stream_id, |
| 255 | + call_id=call_id, |
| 256 | + account_sid=account_sid, |
| 257 | + api_key=os.getenv("EXOTEL_API_KEY"), |
| 258 | + api_token=os.getenv("EXOTEL_API_TOKEN"), |
| 259 | +) |
| 260 | + |
| 261 | +# Configure transport |
| 262 | +transport = FastAPIWebsocketTransport( |
| 263 | + websocket=websocket, |
| 264 | + params=FastAPIWebsocketParams( |
| 265 | + audio_in_enabled=True, |
| 266 | + audio_out_enabled=True, |
| 267 | + add_wav_header=False, |
| 268 | + vad_analyzer=SileroVADAnalyzer(), |
| 269 | + serializer=serializer, |
| 270 | + ), |
| 271 | +) |
| 272 | +``` |
| 273 | + |
| 274 | +<Card |
| 275 | + title="Complete Bot Implementation" |
| 276 | + icon="code" |
| 277 | + href="https://github.com/pipecat-ai/pipecat-examples/blob/main/exotel-chatbot/outbound/bot.py" |
| 278 | +> |
| 279 | + See the full bot.py with outbound call handling and call information usage |
| 280 | +</Card> |
| 281 | + |
| 282 | +### Run the Example |
| 283 | + |
| 284 | +To test dial-out functionality: |
| 285 | + |
| 286 | +1. **Start your server**: Run your FastAPI server |
| 287 | +2. **Trigger a call**: Make an API request to start an outbound call |
| 288 | +3. **Answer your phone**: The bot will call the specified number |
| 289 | +4. **Talk to your bot**: Have a conversation with your AI agent |
| 290 | + |
| 291 | +<Card |
| 292 | + title="Complete Setup Instructions" |
| 293 | + icon="book-open" |
| 294 | + href="https://github.com/pipecat-ai/pipecat-examples/tree/main/exotel-chatbot/outbound" |
| 295 | +> |
| 296 | + See the full README with step-by-step setup, API usage, and outbound call |
| 297 | + configuration |
| 298 | +</Card> |
| 299 | + |
| 300 | +## Key Features |
| 301 | + |
| 302 | +### Audio Format and Sample Rate |
| 303 | + |
| 304 | +Exotel Voice Streaming uses 8kHz mono audio with 16-bit PCM encoding. Configure your pipeline accordingly: |
| 305 | + |
| 306 | +```python |
| 307 | +task = PipelineTask( |
| 308 | + pipeline, |
| 309 | + params=PipelineParams( |
| 310 | + audio_in_sample_rate=8000, |
| 311 | + audio_out_sample_rate=8000, |
| 312 | + allow_interruptions=True, |
| 313 | + ), |
| 314 | +) |
| 315 | +``` |
| 316 | + |
| 317 | +### Built-in Call Information |
| 318 | + |
| 319 | +Like Telnyx, Exotel automatically includes comprehensive call information (to/from numbers, account details) in the WebSocket messages, eliminating the need for custom webhook servers in basic dial-in scenarios. |
| 320 | + |
| 321 | +## Deployment |
| 322 | + |
| 323 | +### Local Development |
| 324 | + |
| 325 | +Use [ngrok](https://ngrok.com/) to expose your local server for testing: |
| 326 | + |
| 327 | +```shell |
| 328 | +python server.py # For dial-out |
| 329 | +# OR |
| 330 | +python bot.py # For dial-in |
| 331 | + |
| 332 | +ngrok http 8000 |
| 333 | +# Use the ngrok URL in your App Bazaar Voicebot applet |
| 334 | +``` |
| 335 | + |
| 336 | +### Pipecat Cloud Deployment |
| 337 | + |
| 338 | +For production deployment without managing your own infrastructure, use Pipecat Cloud: |
| 339 | + |
| 340 | +<Card |
| 341 | + title="Exotel WebSocket on Pipecat Cloud" |
| 342 | + icon="cloud" |
| 343 | + href="/deployment/pipecat-cloud/guides/telephony/exotel-websocket" |
| 344 | +> |
| 345 | + Deploy Exotel WebSocket bots with automatic scaling and managed infrastructure |
| 346 | +</Card> |
| 347 | + |
| 348 | +### Self-Hosted Production Deployment |
| 349 | + |
| 350 | +For fully self-hosted production deployment, ensure your servers are: |
| 351 | + |
| 352 | +- Publicly accessible with HTTPS |
| 353 | +- Able to handle concurrent WebSocket connections |
| 354 | +- Properly configured with Exotel API credentials |
| 355 | +- Implementing proper error handling and logging |
| 356 | + |
| 357 | +## Next Steps |
| 358 | + |
| 359 | +- Explore the [complete examples](https://github.com/pipecat-ai/pipecat-examples/tree/main/exotel-chatbot) for full implementations |
| 360 | +- Learn about [Daily + Twilio SIP integration](./twilio-daily-sip) for advanced telephony scenarios |
| 361 | +- Check out [Daily PSTN integration](./daily-pstn) for direct phone number provisioning |
0 commit comments