How to Authenticate and POST to /v1/call Endpoint in Retell AI for Outbound Twilio Calls
TL;DR
Most outbound call integrations fail at authentication or payload validation. Here's what breaks: missing Bearer tokens, malformed E.164 phone numbers, and agent override misconfiguration. This guide shows you how to authenticate against Retell AI's /v1/call endpoint, structure the request payload correctly, and handle Twilio number registration so your outbound calls actually connect—not timeout or reject.
Prerequisites
API Keys & Credentials
You need a Retell AI API key (generate from your Retell dashboard under Settings → API Keys). Store it in .env as RETELL_API_KEY. You'll also need a Twilio Account SID and Auth Token (found in Twilio Console → Account Info), though Retell handles the Twilio integration directly—you don't call Twilio's API yourself for outbound calls.
System Requirements
Node.js 16+ with npm or yarn. Install the Retell SDK (npm install retell-sdk) or use raw HTTP with fetch/axios. For testing webhooks locally, install ngrok (npm install -g ngrok) to expose your server.
Phone Numbers
Prepare E.164 formatted phone numbers for the recipient (e.g., +14155552671). Your Twilio account must have verified outbound numbers or a purchased phone number assigned.
Server Setup
A basic Node.js/Express server running on localhost:3000 (or your preferred port) to receive webhooks from Retell. HTTPS is required for production webhook endpoints.
Twilio: Get Twilio Voice API → Get Twilio
Step-by-Step Tutorial
Configuration & Setup
Most outbound call failures happen in the first 30 seconds because developers skip authentication validation. Retell AI uses Bearer token auth, and Twilio requires account SID + auth token. Both must be validated BEFORE you attempt a call.
Server Requirements:
- Node.js 18+ (for native fetch)
- Express or Fastify for webhook handling
- Environment variables for secrets (NEVER hardcode)
// server.js - Production-grade setup
import express from 'express';
import crypto from 'crypto';
const app = express();
app.use(express.json());
// Validate environment on startup (fail fast)
const RETELL_API_KEY = process.env.RETELL_API_KEY;
const TWILIO_ACCOUNT_SID = process.env.TWILIO_ACCOUNT_SID;
const TWILIO_AUTH_TOKEN = process.env.TWILIO_AUTH_TOKEN;
if (!RETELL_API_KEY || !TWILIO_ACCOUNT_SID || !TWILIO_AUTH_TOKEN) {
throw new Error('Missing required environment variables');
}
// Health check endpoint (monitors use this)
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: Date.now() });
});
app.listen(3000, () => console.log('Server ready on port 3000'));
Architecture & Flow
Critical distinction: Retell AI handles the voice AI layer. Twilio handles the telephony layer. Your server orchestrates both.
Call Flow (Outbound):
- Your server initiates call via Retell AI API
- Retell AI provisions agent and returns call metadata
- Retell AI connects to Twilio via SIP trunk
- Twilio dials the target phone number
- On answer, Retell AI streams audio bidirectionally
- Webhooks fire for call events (started, ended, analyzed)
What breaks in production: Developers try to use Twilio's REST API directly for voice AI calls. This creates a split-brain scenario where Twilio and Retell AI both think they own the call state. Use Retell AI's /v1/call endpoint ONLY.
Step-by-Step Implementation
1. Create Retell AI Assistant
Before making calls, you need an assistant ID. This defines the AI's behavior, voice, and LLM configuration.
// createAssistant.js - Run once to get assistant_id
async function createAssistant() {
const assistantConfig = {
name: "Outbound Sales Agent",
model: {
provider: "openai",
model: "gpt-4-turbo",
temperature: 0.7,
max_tokens: 500
},
voice: {
provider: "elevenlabs",
voice_id: "21m00Tcm4TlvDq8ikWAM", // Rachel voice
speed: 1.0,
stability: 0.5,
similarity_boost: 0.75
},
transcriber: {
provider: "deepgram",
model: "nova-2",
language: "en-US"
},
response_engine: {
type: "retell-llm",
llm_websocket_url: "wss://your-server.com/llm-websocket"
}
};
try {
const response = await fetch('https://api.retell.ai/v1/assistant', {
method: 'POST',
headers: {
'Authorization': `Bearer ${RETELL_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(assistantConfig)
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Assistant creation failed: ${error.message}`);
}
const assistant = await response.json();
console.log('Assistant ID:', assistant.assistant_id);
return assistant.assistant_id;
} catch (error) {
console.error('Failed to create assistant:', error);
throw error;
}
}
2. Initiate Outbound Call
Critical: Phone numbers MUST be in E.164 format (+1234567890). Missing the + prefix causes silent failures where Twilio accepts the request but never dials.
// initiateCall.js - Production call initiation
async function makeOutboundCall(toNumber, assistantId) {
// Validate E.164 format (prevents 90% of dial failures)
if (!toNumber.match(/^\+[1-9]\d{1,14}$/)) {
throw new Error(`Invalid E.164 format: ${toNumber}`);
}
const callConfig = {
from_number: process.env.TWILIO_FROM_NUMBER, // Your Twilio number
to_number: toNumber,
assistant_id: assistantId,
metadata: {
campaign_id: "Q1_2024_outreach",
lead_source: "webinar_signup"
},
retell_llm_dynamic_variables: {
customer_name: "John Doe",
account_balance: "$1,234.56"
}
};
try {
const response = await fetch('https://api.retell.ai/v1/call', {
method: 'POST',
headers: {
'Authorization': `Bearer ${RETELL_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(callConfig)
});
if (!response.ok) {
const error = await response.json();
// Common error codes: 401 (bad auth), 400 (invalid number), 429 (rate limit)
throw new Error(`Call failed: ${response.status} - ${error.message}`);
}
const call = await response.json();
console.log('Call initiated:', call.call_id);
return call;
} catch (error) {
console.error('Call initiation error:', error);
throw error;
}
}
3. Handle Webhook Events
Retell AI sends webhooks for call lifecycle events. You MUST validate webhook signatures to prevent spoofing attacks.
// webhookHandler.js - Secure webhook processing
app.post('/webhook/retell', async (req, res) => {
const signature = req.headers['x-retell-signature'];
const payload = JSON.stringify(req.body);
// Validate webhook signature (CRITICAL for security)
const expectedSignature = crypto
.createHmac('sha256', process.env.RETELL_WEBHOOK_SECRET)
.update(payload)
.digest('hex');
if (signature !== expectedSignature) {
console.error('Invalid webhook signature');
return res.status(401).send('Unauthorized');
}
const event = req.body;
// Handle different event types
switch (event.event) {
case 'call_started':
console.log(`Call ${event.call_id} started`);
// Update CRM, start recording, etc.
break;
case 'call_ended':
console.log(`Call ${event.call_id} ended. Duration: ${event.call_duration}s`);
// Log to analytics, trigger follow-up workflows
break;
case 'call_analyzed':
// Contains transcript, sentiment, custom analysis
console.log('Call analysis:', event.call_analysis);
break;
default:
console.log('Unknown event type:', event.event);
}
// ALWAYS respond 200 immediately (prevents retries)
res.status(200).send('OK');
});
Error Handling & Edge Cases
Race Condition: If you call /v1/call twice with the same metadata within 5 seconds, Retell AI may deduplicate and return the same call_id. Add a unique `
System Diagram
Call flow showing how Retell AI handles user input, webhook events, and responses.
mermaid
sequenceDiagram
participant User
participant RetellAI
participant SpeechEngine
participant NLPProcessor
participant ErrorHandler
User->>RetellAI: Initiates conversation
RetellAI->>SpeechEngine: Capture audio
SpeechEngine->>RetellAI: Audio stream
RetellAI->>NLPProcessor: Send audio for transcription
NLPProcessor->>RetellAI: Transcription text
RetellAI->>User: Provide response
Note over User,RetellAI: User feedback loop
User->>RetellAI: Provides feedback
RetellAI->>ErrorHandler: Check for errors
ErrorHandler->>RetellAI: Error status
alt Error detected
RetellAI->>User: Notify error
else No error
RetellAI->>User: Continue interaction
end
User->>RetellAI: Ends conversation
RetellAI->>SpeechEngine: Stop audio capture
SpeechEngine->>RetellAI: Confirmation of stop
Testing & Validation
Local Testing with ngrok
Most outbound call failures happen because webhooks can't reach your local server. Retell AI needs a public URL to send call events—localhost won't cut it.
`javascript
// Start ngrok tunnel (run in terminal)
// ngrok http 3000
// Update webhook URL in your call config
const callConfig = {
agent_id: assistant.agent_id,
metadata: {
campaign_id: "test_001",
environment: "development"
},
retell_llm_dynamic_variables: {
customer_name: "Test User"
},
from_number: "+15551234567",
to_number: "+15559876543"
};
// Test the call initiation
const response = await fetch('https://api.retell.ai/v1/call', {
method: 'POST',
headers: {
'Authorization': Bearer ${RETELL_API_KEY},
'Content-Type': 'application/json'
},
body: JSON.stringify(callConfig)
});
if (!response.ok) {
const error = await response.json();
console.error('Call failed:', error.message, error.codes);
// Common: 401 (bad API key), 400 (invalid phone format), 403 (insufficient credits)
}
const call = await response.json();
console.log('Call initiated:', call.call_id, call.status);
`
Real failure mode: If ngrok disconnects mid-call, Retell AI can't send the call-ended event. Your session cleanup never fires. Set a 5-minute TTL on all call sessions to prevent memory leaks.
Webhook Validation
Validate webhook signatures to prevent replay attacks. Retell AI signs payloads with HMAC-SHA256.
`javascript
const crypto = require('crypto');
app.post('/webhook/retell', (req, res) => {
const signature = req.headers['x-retell-signature'];
const payload = JSON.stringify(req.body);
const expectedSignature = crypto
.createHmac('sha256', process.env.RETELL_WEBHOOK_SECRET)
.update(payload)
.digest('hex');
if (signature !== expectedSignature) {
console.error('Invalid webhook signature');
return res.status(401).send('Unauthorized');
}
const event = req.body;
console.log('Valid event:', event.type, event.call_id);
// Process event based on type
if (event.type === 'call_started') {
// Initialize call state
} else if (event.type === 'call_ended') {
// Cleanup and log analysis
console.log('Call analysis:', event.analysis);
}
res.status(200).send('OK');
});
`
Production gotcha: Webhook timeouts after 5 seconds cause Retell AI to retry 3 times with exponential backoff. If your handler does heavy processing (CRM updates, transcription storage), return 200 immediately and queue the work asynchronously. Otherwise, you'll get duplicate events and race conditions on call state updates.
Real-World Example
Barge-In Scenario
Most outbound call systems break when the customer interrupts the agent mid-pitch. Here's what actually happens in production:
Your Retell agent starts reading a 30-second script about account balance. At 8 seconds, the customer says "Wait, what's my balance?" The agent keeps talking for another 2 seconds (TTS buffer lag), then finally stops. By then, the customer has repeated the question twice, and you've burned 4 extra seconds of call time.
`javascript
// Production barge-in handler with buffer flush
app.post('/webhook/retell', async (req, res) => {
const event = req.body;
if (event.event === 'speech_started') {
// Customer started speaking - IMMEDIATELY cancel TTS
const callId = event.call.call_id;
try {
await fetch(`https://api.retell.ai/v2/stop-utterance`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.RETELL_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
call_id: callId,
utterance_id: event.call.last_utterance_id // Critical: stop SPECIFIC utterance
})
});
// Flush audio buffer to prevent stale audio playback
console.log(`[${callId}] Barge-in detected - TTS cancelled at ${Date.now()}`);
} catch (error) {
console.error('Failed to cancel TTS:', error);
// Fallback: agent will finish sentence (bad UX but won't crash)
}
}
res.status(200).send();
});
`
Why this breaks without proper handling: Retell's TTS buffer holds 1-3 seconds of pre-generated audio. If you don't explicitly cancel the utterance, the agent talks over the customer, creating the "robotic conversation" effect that tanks CSAT scores.
Event Logs
Real production webhook payload when barge-in happens:
`javascript
// Event 1: Agent starts speaking (t=0ms)
{
"event": "agent_start_talking",
"call": {
"call_id": "call_abc123",
"last_utterance_id": "utt_xyz789"
},
"timestamp": 1704067200000
}
// Event 2: Customer interrupts (t=1200ms)
{
"event": "speech_started", // THIS is your trigger
"call": {
"call_id": "call_abc123",
"transcript": "" // Empty - speech detected but not transcribed yet
},
"timestamp": 1704067201200
}
// Event 3: Partial transcript arrives (t=1450ms)
{
"event": "transcript",
"call": {
"call_id": "call_abc123",
"transcript": "Wait what's my",
"is_final": false
},
"timestamp": 1704067201450
}
`
Critical timing issue: You have ~250ms between speech_started and the first partial transcript. If you wait for the transcript to decide whether to interrupt, you've already lost 250ms + TTS cancellation latency (100-200ms). Total lag: 350-450ms of the agent talking over the customer.
Edge Cases
False positive from background noise: At default VAD threshold (0.5), a dog barking triggers speech_started. Your agent stops mid-sentence, waits 2 seconds for speech, hears nothing, then awkwardly resumes. Solution: Increase VAD threshold to 0.65 for noisy environments, or implement a 300ms confirmation window before cancelling TTS.
Rapid-fire interruptions: Customer says "Wait... no wait... actually..." (3 interruptions in 5 seconds). Without state tracking, each speech_started event fires a new TTS cancellation request, creating a race condition where the agent never finishes a sentence. Fix: Track isProcessing flag and ignore subsequent interruptions until current turn completes.
`javascript
// Race condition guard for multiple interruptions
let isProcessing = false;
if (event.event === 'speech_started' && !isProcessing) {
isProcessing = true;
await cancelTTS(event.call.call_id);
// Reset flag after turn completes (use 'agent_stop_talking' event)
setTimeout(() => { isProcessing = false; }, 5000); // 5s timeout fallback
}
`
Common Issues & Fixes
Most outbound call failures stem from three production problems: authentication token leaks, E.164 format violations, and race conditions during concurrent call creation. Here's what breaks in production.
Authentication Token Exposure
Problem: Hardcoded API keys in client-side code leak credentials. Retell AI rejects requests with 401 Unauthorized when tokens are rotated or exposed.
Fix: Store credentials server-side only. Never expose RETELL_API_KEY in frontend JavaScript.
`javascript
// Server-side authentication wrapper
async function authenticatedCallRequest(callConfig) {
if (!process.env.RETELL_API_KEY) {
throw new Error('RETELL_API_KEY not configured');
}
try {
const response = await fetch('https://api.retellai.com/v1/call', {
method: 'POST',
headers: {
'Authorization': Bearer ${process.env.RETELL_API_KEY},
'Content-Type': 'application/json'
},
body: JSON.stringify(callConfig)
});
if (response.status === 401) {
throw new Error('Invalid API key - check environment variables');
}
return await response.json();
} catch (error) {
console.error('Auth failed:', error.message);
throw error;
}
}
`
E.164 Phone Number Validation
Problem: Twilio rejects calls when from_number or to_number lack country codes. Error: Invalid phone number format.
Fix: Validate E.164 format before POST. Must start with + and country code (e.g., +14155551234).
javascriptInvalid E.164 format: ${phoneNumber}`);
function validateE164(phoneNumber) {
const e164Regex = /^\+[1-9]\d{1,14}$/;
if (!e164Regex.test(phoneNumber)) {
throw new Error(
}
return phoneNumber;
}
// Use before call creation
const callConfig = {
from_number: validateE164('+14155551234'),
to_number: validateE164('+16505551234'),
assistant_id: assistant.ID
};
`
Race Conditions in Concurrent Calls
Problem: Multiple simultaneous POST requests create duplicate calls when isProcessing flag isn't atomic. Results in double-billing and confused call state.
Fix: Implement request queuing with unique callId tracking.
`javascript
const activeCallIds = new Set();
async function makeOutboundCall(callConfig) {
const callId = ${callConfig.to_number}-${Date.now()};
if (activeCallIds.has(callId)) {
throw new Error('Call already in progress');
}
activeCallIds.add(callId);
isProcessing = true;
try {
const call = await authenticatedCallRequest(callConfig);
return call;
} finally {
activeCallIds.delete(callId);
isProcessing = false;
}
}
`
Production tip: Set isProcessing = false in finally block to prevent deadlocks when requests fail mid-flight.
Complete Working Example
Most developers hit authentication errors or malformed payloads when first calling /v1/call. Here's the full production server that handles Retell AI authentication, Twilio integration, and outbound call orchestration in one copy-paste block.
Full Server Code
This Express server combines assistant creation, authenticated API calls, webhook validation, and E.164 phone number validation. All routes are production-ready with error handling and security checks.
`javascript
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
// Environment variables (set these in .env)
const RETELL_API_KEY = process.env.RETELL_API_KEY;
const TWILIO_ACCOUNT_SID = process.env.TWILIO_ACCOUNT_SID;
const TWILIO_AUTH_TOKEN = process.env.TWILIO_AUTH_TOKEN;
const RETELL_WEBHOOK_SECRET = process.env.RETELL_WEBHOOK_SECRET;
// E.164 validation regex
const e164Regex = /^+[1-9]\d{1,14}$/;
function validateE164(phoneNumber) {
if (!e164Regex.test(phoneNumber)) {
throw new Error(Invalid E.164 format: ${phoneNumber}. Must start with + and country code.);
}
return phoneNumber;
}
// Track active calls to prevent duplicates
const activeCallIds = new Set();
let isProcessing = false;
// Create assistant with Retell AI
async function createAssistant() {
const assistantConfig = {
name: "Outbound Sales Agent",
model: {
provider: "openai",
model: "gpt-4",
temperature: 0.7,
max_tokens: 150
},
voice: {
provider: "elevenlabs",
voice_id: "21m00Tcm4TlvDq8ikWAM",
speed: 1.0,
stability: 0.5,
similarity_boost: 0.75
},
transcriber: {
provider: "deepgram",
language: "en-US"
},
response_engine: {
type: "retell-llm",
llm_websocket_url: "wss://your-server.com/llm-websocket"
}
};
try {
const response = await fetch('https://api.retellai.com/v1/assistant', {
method: 'POST',
headers: {
'Authorization': Bearer ${RETELL_API_KEY},
'Content-Type': 'application/json'
},
body: JSON.stringify(assistantConfig)
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Assistant creation failed: ${error.message}`);
}
const assistant = await response.json();
return assistant.assistant_id;
} catch (error) {
console.error('Assistant creation error:', error);
throw error;
}
}
// Make authenticated outbound call
async function makeOutboundCall(assistantId, toNumber, fromNumber) {
validateE164(toNumber);
validateE164(fromNumber);
const callConfig = {
assistant_id: assistantId,
from_number: fromNumber,
to_number: toNumber,
metadata: {
campaign_id: "spring-2024-promo",
lead_source: "website-form"
},
retell_llm_dynamic_variables: {
customer_name: "John Doe",
account_balance: "$1,234.56"
}
};
try {
const response = await fetch('https://api.retellai.com/v1/call', {
method: 'POST',
headers: {
'Authorization': Bearer ${RETELL_API_KEY},
'Content-Type': 'application/json'
},
body: JSON.stringify(callConfig)
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Call initiation failed: ${error.message} (status: ${response.status})`);
}
const call = await response.json();
activeCallIds.add(call.call_id);
return call;
} catch (error) {
console.error('Outbound call error:', error);
throw error;
}
}
// Webhook signature validation (prevents replay attacks)
function validateWebhookSignature(payload, signature) {
const expectedSignature = crypto
.createHmac('sha256', RETELL_WEBHOOK_SECRET)
.update(JSON.stringify(payload))
.digest('hex');
if (signature !== expectedSignature) {
throw new Error('Invalid webhook signature');
}
}
// Webhook handler for call events
app.post('/webhook/retell', (req, res) => {
const signature = req.headers['x-retell-signature'];
const payload = req.body;
try {
validateWebhookSignature(payload, signature);
const { event, call_id } = payload;
switch (event) {
case 'call_started':
console.log(`Call ${call_id} started`);
break;
case 'call_ended':
console.log(`Call ${call_id} ended`);
activeCallIds.delete(call_id);
break;
case 'call_analyzed':
console.log(`Call ${call_id} analysis:`, payload.analysis);
break;
default:
console.log(`Unknown event: ${event}`);
}
res.status(200).json({ status: 'received' });
} catch (error) {
console.error('Webhook validation failed:', error);
res.status(401).json({ error: 'Unauthorized' });
}
});
// Main route to initiate outbound call
app.post('/initiate-call', async (req, res) => {
if (isProcessing) {
return res.status(429).json({ error: 'Call already in progress' });
}
isProcessing = true;
try {
const { to_number, from_number } = req.body;
// Create assistant (cache this in production)
const assistantId = await createAssistant();
// Make outbound call
const call = await makeOutboundCall(assistantId, to_number, from_number);
res.status(200).json({
status: 'initiated',
call_id: call.call_id,
assistant_id: assistantId
});
} catch (error) {
res.status(500).json({ error: error.message });
} finally {
isProcessing = false;
}
});
// Health check
app.get('/health', (req, res) => {
res.status(200).json({
status: 'healthy',
active_calls: activeCallIds.size
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(Server running on port ${PORT});
});
`
Run Instructions
Environment Setup:
Create .env file with your credentials:
bash
RETELL_API_KEY=your_retell_api_key_here
TWILIO_ACCOUNT_SID=your_twilio_sid_here
TWILIO_AUTH_TOKEN=your_twilio_auth_token_here
RETELL_WEBHOOK_SECRET=your_webhook_secret_here
PORT=3000
Install Dependencies:
bash
npm install express dotenv
Start Server:
bash
node server.js
Test Outbound Call:
`bash
curl -X POST http://localhost:3000/initiate-call \
-H "Content-Type: application/json" \
-d '{
"to_number": "+14155551234",
FAQ
Technical Questions
What authentication method does the /v1/call endpoint require?
The /v1/call endpoint uses Bearer token authentication via the Authorization header. You pass your RETELL_API_KEY (obtained from the Retell dashboard) as Authorization: Bearer ${RETELL_API_KEY}. This is a standard OAuth 2.0 Bearer token pattern. The API validates the token on every request—if it's missing, expired, or invalid, you'll receive a 401 Unauthorized response. Store your RETELL_API_KEY in environment variables, never hardcode it.
How do I format phone numbers for outbound calls?
Phone numbers must be in E.164 format: a + prefix followed by country code and subscriber number (e.g., +14155552671). The validateE164 function uses the regex pattern /^\+[1-9]\d{1,14}$/ to enforce this. Twilio requires E.164 formatting for the from_number and to_number fields in your call payload. Non-compliant numbers will fail silently or return a 400 Bad Request error.
What's the difference between assistantId and assistant object in the call payload?
You can reference an existing assistant by assistantId (a UUID string) or define an inline assistant object with full configuration (model, voice, transcriber). Using assistantId is faster for repeated calls; inline assistant objects are useful for dynamic configuration per call. You cannot use both simultaneously—the API will reject the request if both are present.
Performance
Why is my outbound call latency high?
Latency spikes typically occur during: (1) assistant initialization (100-300ms if not cached), (2) Twilio SIP trunk provisioning (500-1500ms), (3) STT model loading on first request. Mitigate by pre-creating assistants and reusing assistantId instead of inline configs. Monitor webhook response times—if your server takes >5s to acknowledge the call_started event, Retell may timeout and retry, doubling latency.
How many concurrent outbound calls can I make?
This depends on your Retell plan tier and Twilio account limits. Track active calls using activeCallIds (a Set or Map) to prevent resource exhaustion. Each concurrent call consumes STT, TTS, and LLM tokens. Implement backpressure: if activeCallIds.size >= MAX_CONCURRENT, queue the request and process when a call ends.
Platform Comparison
Should I use Retell AI or Twilio's native voice APIs for outbound calls?
Retell AI handles conversational logic (STT, LLM, TTS) natively; Twilio handles carrier routing and SIP trunking. Use Retell's /v1/call endpoint when you need intelligent, context-aware conversations. Use Twilio's Programmable Voice API directly if you need low-level call control (DTMF, call transfers, IVR trees) without AI. Most hybrid setups use Retell for agent override logic and Twilio for fallback PSTN routing.
Can I override the assistant mid-call?
Yes. Use the Agent override feature by sending a webhook event or API call to update the assistant configuration. This is useful for escalating to a human agent or switching conversation modes. However, the override must happen before the call enters the active conversation phase—mid-stream overrides may cause audio glitches or dropped context.
Resources
Retell AI Official Documentation
- API Reference – Complete endpoint specifications, authentication methods, and request/response schemas for /v1/call, /v1/assistant, and webhook event structures.
- Outbound Call Guide – E.164 phone number formatting, call registration requirements, and agent override configurations.
Twilio Integration
- Twilio Voice API Docs – SIP URI handling, call control, and TWILIO_ACCOUNT_SID/TWILIO_AUTH_TOKEN credential setup.
GitHub & Code Examples
- Retell SDK Repository – Production-grade Node.js examples for RETELL_API_KEY authentication and call registration workflows.
Top comments (0)