Client Nexus
12/30/2025, 1:54 PMAI Evy
12/30/2025, 1:54 PMAI Evy
12/30/2025, 1:55 PMvoicemail_reached.
## IVR Navigation (Press Digit)
Your Retell AI agents can automatically navigate IVR menus using DTMF (digit-press) inputs. [(2) ]()
Use the Press Digit node in the agent builder to configure when and what digits to press.
## Getting the Disconnection Reason
To return results to your app, use the [Get Call]() API. The response includes a disconnection_reason field with values like: [(3) ]()
- voicemail_reached
- user_hangup
- agent_hangup
- dial_busy
- dial_no_answer
- And others
javascript
const callResponse = await client.call.retrieve('119c3f8e47135a29e65947eeb34cf12d') ;
console.log(callResponse.disconnection_reason) ;
The call_analysis object in the response also includes an in_voicemail boolean field.
---AI Evy
12/30/2025, 1:55 PMAI Evy
12/30/2025, 1:55 PMClient Nexus
12/30/2025, 4:15 PMAI Evy
12/30/2025, 4:15 PMAI Evy
12/30/2025, 4:16 PMtasks array supports custom fields you can use to track leads. [(1) ]()
Looking at the task object structure, you can use:
- retell_llm_dynamic_variables – Key-value pairs of strings that inject into your prompt. You can store identifiers like campaign_lead_id here.
- metadata – An arbitrary object for storage purposes only. You can put anything here like your internal customer ID or campaign lead ID associated with the call. It's not used for processing but can be retrieved later from the call object. [(2) ]()
Example task structure:
javascript
tasks: [
{
to_number: '+12137774445',
retell_llm_dynamic_variables: {
customer_name: 'John Doe',
campaign_lead_id: 'your_lead_id_here'
}
}
]
[(1) ]()
After the call completes, you can retrieve the metadata and retell_llm_dynamic_variables from the call object using the Get Call API to match results back to your leads.AI Evy
12/30/2025, 4:16 PMAI Evy
12/30/2025, 4:16 PMClient Nexus
12/30/2025, 4:17 PMAI Evy
12/30/2025, 4:17 PMAI Evy
12/30/2025, 4:17 PMrecording_url – URL to the call recording
- duration_ms – Duration of the call in milliseconds
- disconnection_reason – Outcome/error reason (e.g., agent_hangup, user_hangup, dial_failed, voicemail_reached, etc.)
- call_id – Unique identifier for the call
- metadata – Your custom storage object passed when creating the call
- retell_llm_dynamic_variables – Dynamic variables you passed (can include your lead identifier)
javascript
import Retell from 'retell-sdk';
const client = new Retell({
apiKey: 'YOUR_RETELL_API_KEY',
}) ;
const callResponse = await client.call.retrieve('119c3f8e47135a29e65947eeb34cf12d') ;
console.log(callResponse) ;AI Evy
12/30/2025, 4:17 PMmetadata or override_agent_id per individual task. [(2) ]()
However, you can use retell_llm_dynamic_variables to pass identifiers like campaign_lead_id per task, and these will be available when you retrieve the call. [(1) ]()
## 3. Webhook Events for Call Updates
Subscribe to these webhook events: [(3) ]()
| Event Type | Description |
|------------|-------------|
| call_started | Triggered when a call begins |
| call_ended | Triggered when a call completes, transfers, or errors |
| call_analyzed | Triggered when post-call analysis completes |
The call_ended webhook includes all fields from the call object except call_analysis.
Sample webhook payload:
json
{
"event": "call_ended",
"call": {
"call_type": "phone_call",
"from_number": "+12137771234",
"to_number": "+12137771235",
"direction": "inbound",
"call_id": "Jabr9TXYYJHfvl6Syypi88rdAHYHmcq6",
"agent_id": "oBeDLoLOeuAbiuaMFXRtDOLriTJ5tSxD",
"call_status": "registered",
"metadata": {},
"retell_llm_dynamic_variables": {
"customer_name": "John Doe"
},
"start_timestamp": 1714608475945,
"end_timestamp": 1714608491736,
"disconnection_reason": "user_hangup",
"transcript": "..."
}
}AI Evy
12/30/2025, 4:17 PMmetadata and retell_llm_dynamic_variables are not provided, they will be omitted from the webhook payload
## Recommended Approach
Since per-task metadata isn't supported in batch calls yet, use retell_llm_dynamic_variables to pass your campaign_lead_id, then match it back in your call_ended webhook handler or when polling via Get Call API.AI Evy
12/30/2025, 4:17 PMAI Evy
12/30/2025, 4:17 PMClient Nexus
12/30/2025, 5:20 PMAI Evy
12/30/2025, 5:20 PMAI Evy
12/30/2025, 5:20 PMend_call_after_silence_ms to a shorter duration to end calls faster when leads don't respond. [(2) ]()
## Reminder System for Unresponsive Users
Configure the agent to prompt silent users before ending: [(1) ]()
- reminder_trigger_ms – Triggers a reminder if the user is silent for the specified duration after agent speech. Default is 10,000 ms (10 seconds) .
- reminder_max_count – Controls how many times the agent will remind an unresponsive user. Default is 1 (remind once) . Set to 0 to disable reminders entirely.
## Maximum Call Duration
Set max_call_duration_ms to force-end calls that run too long:
- Minimum: 60,000 ms (1 minute)
- Maximum: 7,200,000 ms (2 hours)
- Default: 3,600,000 ms (1 hour)
## Voicemail Detection Timeout
voicemail_detection_timeout_ms configures when to stop running voicemail detection, since it becomes unlikely to hit voicemail after a couple minutes.
- Minimum: 5,000 ms (5 seconds)
- Maximum: 180,000 ms (3 minutes)
- Default: 30,000 ms (30 seconds)
These settings combined let you automatically end calls with no response, limit wasted time on unproductive calls, and get corresponding disconnection_reason values for tracking in your app.AI Evy
12/30/2025, 5:20 PMAI Evy
12/30/2025, 5:20 PMClient Nexus
12/30/2025, 5:31 PMAI Evy
12/30/2025, 5:31 PMAI Evy
12/30/2025, 5:31 PMenable_voicemail_detection – Detects whether the call enters a voicemail (phone calls only) [(1) ]()
- voicemail_option.action.type – Supports static_text (leave message) or hang_up
- voicemail_detection_timeout_ms – Valid range: 5,000 ms to 180,000 ms, default 30,000 ms
Silence/Reminder Settings:
- reminder_trigger_ms – Triggers reminder after user silence, default 10,000 ms
- reminder_max_count – Controls reminder count, default 1, set to 0 to disable
- end_call_after_silence_ms – Minimum 10,000 ms, default 600,000 ms
Voicemail Option Example Structure:
json
{
"action": {
"type": "static_text",
"text": "Please give us a callback tomorrow at 10am."
}
}
Your webhook handling for disconnection_reason with voicemail_reached is the correct approach.
Regarding IVR navigation: the API documentation for agent configuration does not expose a direct parameter for IVR digit navigation in the agent create/update endpoints. [(2) ]() , [(3) ]()AI Evy
12/30/2025, 5:31 PMAI Evy
12/30/2025, 5:31 PMClient Nexus
12/30/2025, 6:03 PMAI Evy
12/30/2025, 6:03 PMAI Evy
12/30/2025, 6:03 PMtrigger_timestamp as Unix timestamp in milliseconds) [(1) ]()
- Call time windows to restrict when calls can run (specific hours and days of the week) [(2) ]()
javascript
const batchCallResponse = await client.batchCall.createBatchCall({
from_number: '+14157774444',
tasks: [{ to_number: '+12137774445' }],
}) ;
[(1) ]()
Your Approach Works Because:
1. You're using Retell's call_ended webhook to trigger your next step calculation
2. Your cron job (process-campaign-steps) creates new batch calls when scheduled_for <= now()
3. Each step triggers a separate Retell batch call API request
Key Integration Points:
- Use retell_llm_dynamic_variables per task to track your campaign_lead_id so you can match call results back to your scheduled steps
- Listen for call_ended webhooks to update your scheduled_campaign_steps table and calculate the next step's scheduledFor
- Your stop conditions (campaign active, contact replied, manually stopped) are handled before calling the Retell API—this is correct
Retell doesn't provide built-in multi-step campaign sequencing with delays, so your external orchestration via Supabase + cron is the right pattern.AI Evy
12/30/2025, 6:03 PMAI Evy
12/30/2025, 6:03 PMClient Nexus
12/30/2025, 6:48 PMAI Evy
12/30/2025, 6:48 PMAI Evy
12/30/2025, 6:49 PM///## Tool Usage Instructions
1. Gather initial information about the customer's issue.
## Using Dynamic Variables for Context
*Option C (Dynamic Variables) * works well for passing call-specific context like customer type. Dynamic variables can be used in tool descriptions and are injected at runtime. [(2) ]()
You can pass context like:
json
{
"customer_name": "John Smith",
"customer_type": "enterprise"
}AI Evy
12/30/2025, 6:49 PMAI Evy
12/30/2025, 6:50 PMAI Evy
12/30/2025, 6:50 PM{{customer_type}} in your tool description to help the LLM make the right selection.
## Architecture Recommendation
Have the AI return a calendar_id parameter when calling the booking tool. This is cleaner because:
1. The LLM makes the selection based on your prompt instructions
2. Your backend receives a deterministic calendar ID
3. You can validate the selection server-side
The Cal.com integration requires an event_type_id for each calendar/event type. [(3) ]() If you have multiple calendars, you'd configure separate tools or have the AI pass the appropriate ID based on your prompt logic.AI Evy
12/30/2025, 6:51 PMjavascript
nodes: [
{
id: 'collect_info',
type: 'conversation',
instruction: {
type: 'prompt',
text: 'Ask the customer for their name and contact information.'
}
}
]
### Using LLM States for Multi-Step Collection
If using Retell LLM (single/multi-prompt agent) , the states feature helps reduce prompt length and tool choices when the call can be broken into distinct states: [(2) ]()
javascript
states: [
{
name: 'information_collection',
state_prompt: 'You will follow the steps below to collect information...',
edges: [
{
destination_state_name: 'appointment_booking',
description: 'Transition to book an appointment.'
}
]
}
]
### Tool Parameter Structure
For the booking tool itself, custom tools support parameters defined as an object with properties: [(3) ]()
javascript
parameters: {
type: 'object',
properties: {
message: {
type: 'string',AI Evy
12/30/2025, 6:51 PMAI Evy
12/30/2025, 6:51 PMClient Nexus
12/30/2025, 6:51 PMAI Evy
12/30/2025, 6:51 PMparameters object, or use a nested structure depending on your backend requirements.
### Summary
- Use conversation flow nodes or LLM states to guide the AI through collecting party size, special requests, reservation type, and time preferences
- Define your booking tool with structured parameters (either flat or nested object)
- Transition to the booking state/node only after required information is collected
---AI Evy
12/30/2025, 6:52 PMAI Evy
12/30/2025, 6:52 PMClient Nexus
12/30/2025, 6:52 PMAI Evy
12/30/2025, 6:52 PMAI Evy
12/30/2025, 6:52 PMAI Evy
12/30/2025, 6:55 PMAI Evy
12/30/2025, 6:55 PMevent_type_id for each calendar/event type. [(3) ]()
*Option C (Two-Step Flow) * provides better accuracy for complex scenarios. You can:
1. First tool call: get_available_calendars returns list of calendars with IDs
2. Second tool call: check_calendar_availability or book_appointment with the selected calendar's event_type_id
The LLM will process the first tool's response and use that information to make the second call. [(1) ]()
## Tool Call Result Handling
When implementing multi-step flows, send tool call results back through the WebSocket:
json
{
"response_type": "tool_call_result",
"tool_call_id": "unique_id",
"content": "result_string"
}AI Evy
12/30/2025, 6:56 PMcontent field. The LLM will process this and generate a natural response.
Recommended response structure:
json
{
"confirmation_number": "RES-12345",
"status": "confirmed",
"party_size": 4,
"date": "2025-01-15",
"time": "7:00 PM",
"special_requests": "window seat, dairy allergy",
"next_steps": "Arrive 10 minutes early. Cancellation requires 24-hour notice."
}
### Why Let the AI Generate Confirmation Naturally
Based on how Retell handles tool results, returning structured data (Option B/C) is better than a verbatim speak_message because:
1. The LLM receives the tool result and incorporates it naturally into conversation
2. The AI can adapt tone and phrasing to match the conversation context
3. You avoid robotic-sounding verbatim readings
### Include Next Steps (Option C)
Including next_steps or follow-up instructions in your response gives the AI additional context to communicate naturally—e.g., "You're all set for 7 PM on January 15th for 4 guests. Just a heads up, please arrive 10 minutes early and let us know at least 24 hours ahead if you need to cancel."
Key point: The AI will use the returned data to construct its confirmation message, so include all details you want communicated.AI Evy
12/30/2025, 6:56 PMClient Nexus
12/30/2025, 6:56 PMAI Evy
12/30/2025, 6:56 PMAI Evy
12/30/2025, 6:58 PMAI Evy
12/30/2025, 6:58 PMcheck_calendar_availability function supports an optional timezone parameter that you can set during configuration. [(1) ]()
From the setup steps:
> "(optional) : add a timezone to be used in the function"
### Why Backend Default is Preferred
1. Simpler tool definition – Keep timezone as an optional parameter in your tool. If the AI doesn't pass it, your backend applies the business default.
2. Consistent behavior – Conditionally modifying tool parameters (required vs optional) between calls can introduce complexity. The LLM receives the tool definition at conversation start, so dynamically changing requirements mid-conversation isn't straightforward.
3. Fallback logic – Your backend can check: if timezone is provided, use it; otherwise, apply the business default timezone.
### Implementation Pattern
For your two modes:
- **Auto-detect mode**: Include timezone as an optional parameter. Instruct the AI in your prompt to ask for timezone if the customer hasn't mentioned it.
- **Default timezone mode**: Include timezone as optional. Your backend applies the default when the parameter is omitted.
This keeps your tool definition stable while letting your backend handle the conditional logic based on your mode setting.AI Evy
12/30/2025, 6:58 PMAI Evy
12/30/2025, 6:58 PMbook_appointment_cal tool type includes:
[(3) ]()
json
{
"type": "book_appointment_cal",
"name": "book_appointment",
"description": "Book an annual check up.",
"cal_api_key": "cal_live_xxxxxxxxxxxx",
"event_type_id": 60444,
"timezone": "America/Los_Angeles"
}
## Important Note on {{current_time}}
Community feedback indicates you should include {{current_time}} in your prompt for date handling:
[(4) ]()
> "You have to use {{current_time}} in your prompt"
> "You can say something like: This is today's date and time: {{current_time}}. Please consider this in checking calendar availability and booking."
This helps prevent issues with relative dates ("tomorrow", "next week") being misinterpreted.
[(5) ]()
Your parameter structure looks correct. The main adjustment would be ensuring {{current_time}} is referenced in your prompts for accurate date handling.AI Evy
12/30/2025, 6:58 PMAI Evy
12/30/2025, 6:58 PMAI Evy
12/30/2025, 6:58 PMClient Nexus
12/30/2025, 6:58 PMClient Nexus
12/30/2025, 6:58 PMAI Evy
12/30/2025, 6:58 PMAI Evy
12/30/2025, 6:58 PMClient Nexus
12/31/2025, 6:55 PMAI Evy
12/31/2025, 6:55 PMAI Evy
12/31/2025, 6:55 PMfrom_number (required) - E.164 format
- tasks (required) - Array of call tasks
- name (optional) - For your reference
Each task supports:
- to_number
- retell_llm_dynamic_variables
- metadata
## Webhook Events for Tracking
Retell sends these webhook events: [(2) ]()
| Event Type | Description |
|------------|-------------|
| call_started | Triggered when a new call begins |
| call_ended | Triggered when a call completes, transfers, or encounters an error |
| call_analyzed | Triggered when call analysis is complete |
Important: If the call did not connect (like dial_failed) , the call_started webhook will not be triggered, but call_ended and call_analyzed will still fire.
## Likely Issue: Metrics Not Updating
Your webhook flow looks correct. If metrics aren't updating in your app but appear in Retell, check:
1. Webhook delivery - Retell has a 10-second timeout and retries up to 3 times if no 2xx response is received
2. Matching call data - Use the metadata and retell_llm_dynamic_variables you pass to match calls back to your campaign records. These are returned in the webhook payload.
3. Manual sync fallback - You can retrieve call details using the Get Call API with the call_id [(3) ]() or List Calls API [(4) ]() to pull results if webhooks fail.
Regarding batch call completion notifications: there's no documented batch-level webhook event when all calls finish. [(5) ](AI Evy
12/31/2025, 6:55 PMcall_ended events or poll the API.AI Evy
12/31/2025, 6:55 PMAI Evy
12/31/2025, 6:55 PM