Overview
The Voice Calls API integrates with Retell AI to provide intelligent voice call handling, including triage, appointment scheduling, and conversation management.
The Call Object
Pet name mentioned in call
Call direction: inbound or outbound
Call status: completed, transferred, missed, voicemail, emergency
AI-generated call summary
Triage urgency: emergency, urgent, routine, info
Detected symptom keywords
Structured transcript with speaker labels
Detailed triage information
Whether appointment was booked (appointment ID or boolean)
Reason for transfer to human agent
Veterinarian involved (if transferred)
Whether follow-up email was sent
TranscriptMessage Object
Timestamp in seconds from call start
TriageData Object
Create Web Call
Initiate a web-based voice call using Retell AI.
Endpoint
POST /api/retell/create-web-call
Request
curl -X POST http://localhost:3000/api/retell/create-web-call \
-H "Content-Type: application/json" \
-d '{
"agentId": "agent_abc123xyz"
}'
Request Parameters
Retell AI agent ID (uses RETELL_AGENT_ID env variable if not provided)
Response
{
"callId": "call_abc123xyz",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"agentId": "agent_abc123xyz"
}
Unique call identifier from Retell
JWT access token for this call session
Agent ID used for this call
Start Call with Retell SDK
import { RetellWebClient } from 'retell-client-js-sdk';
const retellClient = new RetellWebClient();
// Create web call
const { callId, accessToken } = await fetch('/api/retell/create-web-call', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ agentId: 'agent_abc123' })
}).then(r => r.json());
// Set up event listeners
retellClient.on('call_started', () => {
console.log('Call started');
});
retellClient.on('call_ended', () => {
console.log('Call ended');
});
retellClient.on('agent_start_talking', () => {
console.log('Agent speaking');
});
retellClient.on('agent_stop_talking', () => {
console.log('Agent stopped speaking');
});
retellClient.on('update', (update) => {
if (update.transcript) {
console.log('Transcript update:', update.transcript);
}
});
retellClient.on('error', (error) => {
console.error('Call error:', error);
});
// Start the call
await retellClient.startCall({
accessToken: accessToken
});
// Later: end the call
retellClient.stopCall();
Process Call
Retrieve call details after completion.
Endpoint
POST /api/retell/process-call
Request
curl -X POST http://localhost:3000/api/retell/process-call \
-H "Content-Type: application/json" \
-d '{
"callId": "call_abc123xyz"
}'
Response
{
"success": true,
"callId": "call_abc123xyz",
"status": "completed",
"duration": 127,
"transcript": "AI: Hello, this is Luna from Paw and Care Veterinary. How can I help you today?\nCaller: Hi, I'm calling about my dog Buddy. He's been vomiting...\n"
}
List Calls
curl -X GET http://localhost:3000/api/calls \
-H "Authorization: Bearer $TOKEN"
Response
[
{
"id": "call-001",
"ownerId": "owner-123",
"ownerName": "John Smith",
"petName": "Buddy",
"direction": "inbound",
"status": "completed",
"durationSeconds": 127,
"summary": "Owner called about Buddy (dog) experiencing vomiting and lethargy. Symptoms started 2 days ago. Appointment scheduled for tomorrow at 2:30 PM with Dr. Chen. Triaged as urgent.",
"triageLevel": "urgent",
"triageKeywords": ["vomiting", "lethargy"],
"transcript": "AI: Hello, this is Luna...\n",
"transcriptMessages": [
{ "speaker": "AI", "text": "Hello, this is Luna from Paw and Care. How can I help?", "timestamp": 0 },
{ "speaker": "Caller", "text": "Hi, my dog has been vomiting.", "timestamp": 5 }
],
"triageData": {
"petName": "Buddy",
"species": "dog",
"symptoms": ["vomiting", "lethargy"],
"duration": "2 days",
"urgency": "urgent",
"urgencyScore": 7
},
"appointmentBooked": "appt-456",
"startedAt": "2024-12-15T10:00:00Z",
"endedAt": "2024-12-15T10:02:07Z",
"followUpEmailSent": true
}
]
Get Call by ID
curl -X GET http://localhost:3000/api/calls/call-001 \
-H "Authorization: Bearer $TOKEN"
Webhook Events
Handle Retell webhook events:
Endpoint
Webhook Events
call_started - Call has started
call_ended - Call has ended
call_analyzed - Call analysis complete
Example Webhook Handler
app.post('/api/retell/webhook', (req, res) => {
const event = req.body;
switch (event.event) {
case 'call_started':
console.log('Call started:', event.call.call_id);
break;
case 'call_ended':
console.log('Call ended:', event.call.call_id);
// Process and save call data
processCompletedCall(event.call);
break;
case 'call_analyzed':
console.log('Call analyzed:', event.call.call_id);
// Extract insights from analysis
break;
}
res.json({ received: true });
});
Complete Call Flow Example
import { RetellWebClient } from 'retell-client-js-sdk';
import { supabase } from '@/lib/supabase';
class VoiceCallManager {
private retellClient: RetellWebClient;
private callId: string | null = null;
private transcript: TranscriptMessage[] = [];
private triageData: any = {};
async startCall() {
// 1. Create web call
const response = await fetch('/api/retell/create-web-call', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
const { callId, accessToken } = await response.json();
this.callId = callId;
// 2. Initialize Retell client
this.retellClient = new RetellWebClient();
this.retellClient.on('update', (update) => {
if (update.transcript) {
this.transcript = update.transcript.map((t: any, i: number) => ({
speaker: t.role === 'agent' ? 'AI' : 'Caller',
text: t.content,
timestamp: i * 5
}));
// Extract triage data
this.updateTriageData();
}
});
this.retellClient.on('call_ended', async () => {
await this.saveCall();
});
// 3. Start call
await this.retellClient.startCall({ accessToken });
}
private updateTriageData() {
const fullText = this.transcript.map(t => t.text).join(' ');
// Detect symptoms
const symptoms = [];
if (fullText.toLowerCase().includes('vomit')) symptoms.push('vomiting');
if (fullText.toLowerCase().includes('lethar')) symptoms.push('lethargy');
this.triageData = {
symptoms,
urgencyScore: symptoms.length * 3,
urgency: symptoms.length > 2 ? 'urgent' : 'routine'
};
}
private async saveCall() {
const { error } = await supabase.from('calls').insert({
id: this.callId,
direction: 'inbound',
status: 'completed',
duration_seconds: this.transcript.length * 5,
transcript: this.transcript.map(t => `${t.speaker}: ${t.text}`).join('\n'),
triage_level: this.triageData.urgency,
triage_keywords: this.triageData.symptoms,
started_at: new Date().toISOString(),
ended_at: new Date().toISOString()
});
if (error) console.error('Failed to save call:', error);
}
endCall() {
this.retellClient.stopCall();
}
}