
Support
+91 73375 92673Quick note
Compare metered billing against unlimited Vani TTS before you pick a plan.

Support
+91 73375 92673Quick note
Compare metered billing against unlimited Vani TTS before you pick a plan.
Building production-ready AI voice agents requires connecting powerful AI models to reliable telephony infrastructure. Retell AI provides state-of-the-art conversational AI, while Twilio offers enterprise-grade phone services. Together, they create a robust platform for AI voice automation.
This comprehensive tutorial walks you through integrating Retell AI with Twilio, from initial setup to production deployment.
Before starting, you'll need:
Here's how the integration works:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Caller │ ◄─────► │ Twilio │ ◄─────► │ Retell AI │
│ (Phone) │ Voice │ (Telephony)│ WebRTC │ (AI Agent) │
└─────────────┘ └─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ Your Server │
│ (Webhooks) │
└─────────────┘
Flow:
# Install Twilio CLI
npm install -g twilio-cli
# Login to Twilio
twilio login
# Buy a phone number (US example)
twilio phone-numbers:buy:mobile --country-code US
Or purchase through the Twilio Console.
Navigate to Twilio Console and copy:
// Store in .env file
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=your_auth_token_here
TWILIO_PHONE_NUMBER=+1234567890
// Add to .env file
RETELL_API_KEY=your_retell_api_key_here
// create-agent.js
import axios from 'axios';
const RETELL_API_KEY = process.env.RETELL_API_KEY;
async function createAgent() {
const response = await axios.post(
'https://api.retellai.com/v1/agent',
{
name: 'Customer Support Agent',
voice_id: 'elevenlabs-rachel', // Choose from available voices
language: 'en-US',
prompt: `You are a helpful customer support agent for Acme Corp.
You can help with:
- Order status inquiries
- Product information
- Appointment scheduling
- General questions
Be friendly, professional, and concise.`,
// Optional: Add function calling
functions: [
{
name: 'check_order_status',
description: 'Check the status of a customer order',
parameters: {
type: 'object',
properties: {
order_id: {
type: 'string',
description: 'The order ID to check'
}
},
required: ['order_id']
}
}
],
// Webhook for function calls
webhook_url: 'https://your-domain.com/webhooks/retell-functions'
},
{
headers: {
'Authorization': `Bearer ${RETELL_API_KEY}`,
'Content-Type': 'application/json'
}
}
);
console.log('Agent created:', response.data);
return response.data.agent_id;
}
createAgent();
Save the returned agent_id for later use.
mkdir retell-twilio-integration
cd retell-twilio-integration
npm init -y
# Install dependencies
npm install express twilio axios dotenv
npm install --save-dev nodemon
// server.js
import express from 'express';
import twilio from 'twilio';
import axios from 'axios';
import dotenv from 'dotenv';
dotenv.config();
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
const PORT = process.env.PORT || 3000;
// Twilio credentials
const TWILIO_ACCOUNT_SID = process.env.TWILIO_ACCOUNT_SID;
const TWILIO_AUTH_TOKEN = process.env.TWILIO_AUTH_TOKEN;
const twilioClient = twilio(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);
// Retell AI credentials
const RETELL_API_KEY = process.env.RETELL_API_KEY;
const RETELL_AGENT_ID = process.env.RETELL_AGENT_ID;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
// Add to server.js
app.post('/webhooks/twilio-inbound', async (req, res) => {
try {
const { CallSid, From, To } = req.body;
console.log(`Incoming call from ${From} to ${To}`);
// Create Retell AI call session
const retellResponse = await axios.post(
'https://api.retellai.com/v1/call',
{
agent_id: RETELL_AGENT_ID,
audio_encoding: 'mulaw',
sample_rate: 8000,
from_number: From,
to_number: To,
metadata: {
twilio_call_sid: CallSid
}
},
{
headers: {
'Authorization': `Bearer ${RETELL_API_KEY}`,
'Content-Type': 'application/json'
}
}
);
const { call_id, access_token } = retellResponse.data;
// Generate TwiML to connect call to Retell AI
const twiml = new twilio.twiml.VoiceResponse();
const connect = twiml.connect();
connect.stream({
url: `wss://api.retellai.com/v1/ws?call_id=${call_id}&access_token=${access_token}`
});
res.type('text/xml');
res.send(twiml.toString());
} catch (error) {
console.error('Error handling inbound call:', error);
// Fallback TwiML
const twiml = new twilio.twiml.VoiceResponse();
twiml.say('Sorry, we are experiencing technical difficulties. Please try again later.');
res.type('text/xml');
res.send(twiml.toString());
}
});
Set your Twilio number's webhook URL:
# Using Twilio CLI
twilio phone-numbers:update +1234567890 \
--voice-url="https://your-domain.com/webhooks/twilio-inbound"
# Or configure in Twilio Console:
# Phone Numbers → Manage → Active Numbers → Select Number
# Voice & Fax → A Call Comes In → Webhook → POST
# URL: https://your-domain.com/webhooks/twilio-inbound
// Add to server.js
app.post('/api/make-call', async (req, res) => {
try {
const { to_number, from_number } = req.body;
// Validate phone numbers
if (!to_number || !from_number) {
return res.status(400).json({ error: 'Missing phone numbers' });
}
// Create Retell AI call session
const retellResponse = await axios.post(
'https://api.retellai.com/v1/call',
{
agent_id: RETELL_AGENT_ID,
audio_encoding: 'mulaw',
sample_rate: 8000,
from_number: from_number,
to_number: to_number
},
{
headers: {
'Authorization': `Bearer ${RETELL_API_KEY}`,
'Content-Type': 'application/json'
}
}
);
const { call_id, access_token } = retellResponse.data;
// Initiate Twilio call
const call = await twilioClient.calls.create({
to: to_number,
from: from_number,
url: `https://your-domain.com/webhooks/twilio-outbound?call_id=${call_id}&access_token=${access_token}`,
statusCallback: `https://your-domain.com/webhooks/twilio-status`,
statusCallbackEvent: ['initiated', 'ringing', 'answered', 'completed']
});
res.json({
success: true,
call_id: call_id,
twilio_call_sid: call.sid
});
} catch (error) {
console.error('Error making outbound call:', error);
res.status(500).json({ error: 'Failed to initiate call' });
}
});
// Outbound call webhook
app.post('/webhooks/twilio-outbound', (req, res) => {
const { call_id, access_token } = req.query;
const twiml = new twilio.twiml.VoiceResponse();
const connect = twiml.connect();
connect.stream({
url: `wss://api.retellai.com/v1/ws?call_id=${call_id}&access_token=${access_token}`
});
res.type('text/xml');
res.send(twiml.toString());
});
curl -X POST http://localhost:3000/api/make-call \
-H "Content-Type: application/json" \
-d '{
"to_number": "+1234567890",
"from_number": "+0987654321"
}'
// Add to server.js
app.post('/webhooks/retell-functions', async (req, res) => {
try {
const { call_id, function_name, parameters } = req.body;
console.log(`Function call: ${function_name}`, parameters);
let result;
switch (function_name) {
case 'check_order_status':
result = await checkOrderStatus(parameters.order_id);
break;
case 'book_appointment':
result = await bookAppointment(parameters);
break;
default:
result = { error: 'Unknown function' };
}
// Return result to Retell AI
res.json({
result: result
});
} catch (error) {
console.error('Error handling function call:', error);
res.status(500).json({ error: 'Function execution failed' });
}
});
// Example function implementations
async function checkOrderStatus(orderId) {
// Query your database
const order = await db.orders.findOne({ id: orderId });
if (!order) {
return { status: 'not_found', message: 'Order not found' };
}
return {
status: 'success',
order_id: orderId,
order_status: order.status,
estimated_delivery: order.estimatedDelivery,
tracking_number: order.trackingNumber
};
}
async function bookAppointment(params) {
const { date, time, service_type } = params;
// Create appointment in your system
const appointment = await db.appointments.create({
date,
time,
serviceType: service_type,
status: 'confirmed'
});
return {
status: 'success',
appointment_id: appointment.id,
confirmation_number: appointment.confirmationNumber
};
}
// Add to server.js
app.post('/webhooks/twilio-status', (req, res) => {
const { CallSid, CallStatus, CallDuration } = req.body;
console.log(`Call ${CallSid} status: ${CallStatus}`);
// Log to database
db.callLogs.create({
twilioCallSid: CallSid,
status: CallStatus,
duration: CallDuration,
timestamp: new Date()
});
res.sendStatus(200);
});
// Add to server.js
app.post('/webhooks/retell-events', (req, res) => {
const { event_type, call_id, data } = req.body;
console.log(`Retell event: ${event_type}`, data);
switch (event_type) {
case 'call_started':
console.log(`Call ${call_id} started`);
break;
case 'call_ended':
console.log(`Call ${call_id} ended. Duration: ${data.duration}s`);
// Save transcript, analytics, etc.
saveCallData(call_id, data);
break;
case 'call_analyzed':
console.log(`Call ${call_id} analyzed`, data.analysis);
break;
}
res.sendStatus(200);
});
async function saveCallData(callId, data) {
await db.calls.create({
retellCallId: callId,
transcript: data.transcript,
duration: data.duration,
sentiment: data.sentiment,
summary: data.summary,
timestamp: new Date()
});
}
# Install ngrok
npm install -g ngrok
# Start your server
npm start
# In another terminal, start ngrok
ngrok http 3000
Copy the ngrok URL (e.g., https://abc123.ngrok.io) and update:
https://abc123.ngrok.io/webhooks/twilio-inboundhttps://abc123.ngrok.io/webhooks/retell-functionscurl -X POST http://localhost:3000/api/make-call \
-H "Content-Type: application/json" \
-d '{
"to_number": "+1234567890",
"from_number": "+0987654321"
}'
Have a conversation that triggers a function:
You: "What's the status of order 12345?"
AI: [Calls check_order_status function]
AI: "Your order 12345 is currently in transit and will arrive on Friday."
# .env.production
NODE_ENV=production
PORT=3000
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=your_auth_token
TWILIO_PHONE_NUMBER=+1234567890
RETELL_API_KEY=your_retell_api_key
RETELL_AGENT_ID=your_agent_id
DATABASE_URL=postgresql://...
Option 1: AWS Elastic Beanstalk
eb init
eb create production
eb deploy
Option 2: Heroku
heroku create your-app-name
git push heroku main
Option 3: DigitalOcean App Platform
doctl apps create --spec app.yaml
Update Twilio and Retell AI webhooks to use your production domain:
https://your-domain.com/webhooks/twilio-inboundhttps://your-domain.com/webhooks/retell-functionsSolution: Check audio encoding settings match:
{
audio_encoding: 'mulaw', // Must match Twilio
sample_rate: 8000 // Must match Twilio
}
Solution:
Solution:
Solution:
You now have a complete Retell AI and Twilio integration! This setup provides:
✅ Inbound and outbound call handling
✅ Function calling for real-world actions
✅ Event tracking and logging
✅ Production-ready architecture
✅ Error handling and fallbacks
Ready to build your own AI voice agents? Get started with VaniAgent for a fully managed solution with Twilio integration built-in.
Deploy AI voice agents in minutes and build outbound, inbound, and follow-up workflows on one platform.
Master LLM function calling for intelligent AI voice agents. Learn tool integration, API calls, real-time data access & building context-aware voice automation.
Achieve sub-500ms voice latency with edge computing & WebSocket optimization. Technical deep dive into building natural AI conversations with minimal delay.