Webhooks allow your application to receive real-time notifications about events that occur in your Retell AI account. Instead of continuously polling our API, webhooks push data to your application as events happen, making your integrations more efficient and responsive.
Event Types
Retell AI supports the following webhook events for voice calls:
If the call did not connect (like dial failed), the call_started webhook event will not be triggered.
| Event Type | Description | Payload |
|---|
call_started | Triggered when a new call begins | Basic call information |
call_ended | Triggered when a call completes, transfers, or encounters an error | all fields from the call object except call_analysis. |
call_analyzed | Triggered when call analysis is complete | Full call data including call_analysis object |
transcript_updated | Triggered on turn-taking transcript updates, plus a final update when the call ends | Full call data plus transcript_with_tool_calls |
transfer_started | Triggered when a transfer is initiated | Full call data plus transfer_destination and transfer_option (if available) |
transfer_bridged | Triggered when a transfer successfully bridges | Full call data plus transfer_destination and transfer_option (if available) |
transfer_cancelled | Triggered when a transfer is cancelled or fails to connect | Full call data plus transfer_destination and transfer_option (if available) |
transfer_ended | Triggered when the transfer leg ends | Full call data |
Retell AI supports the following webhook events for chat:
| Event Type | Description | Payload |
|---|
chat_started | Triggered when a new chat begins | Basic chat information |
chat_ended | Triggered when a chat completes or errors out | All fields from the chat object except chat_analysis. |
chat_analyzed | Triggered when chat analysis is complete | Full chat data including chat_analysis object |
Common Use Cases
-
Real-time Analytics
- Track call statistics and performance metrics
- Monitor call volumes and patterns
- Trigger alerts for specific call outcomes
-
System Integration
- Update CRM records when calls complete
- Trigger workflow automations based on call analysis
- Archive call transcripts in your data warehouse
-
Call Monitoring
- Get notified of failed or transferred calls
- Track call duration and completion status
- Monitor agent performance in real-time
Webhook Spec
The webhook will POST the payload to your endpoint. The webhook has a timeout of 10 seconds. If within 10 seconds no success status (2xx) is received, the webhook will be retried, up to 3 times.
The webhook will be triggered in order, but is not blocking. For example, if the webhook for call_started is not successful, we can still trigger call_ended webhook.
When the call did not connect (like calls with dial_failed, dial_no_answer, dial_busy disconnection reason), it will not have its call_started webhook triggered. It will still have its call_ended and call_analyzed webhook triggered.
Request payload
The webhook will contain the event type and the call object. See a sample payload in the “Handle Webhook” section below.
Event filtering
You can limit which events are delivered per agent using the webhook_events field when creating or updating an agent.
- Voice agent default:
call_started, call_ended, call_analyzed
- Chat agent default:
chat_started, chat_ended, chat_analyzed
This is useful when you only need high-signal events and want to reduce webhook traffic.
Register Webhook
We offer two types of webhooks: Agent-Level and Account-Level, designed to streamline your event notification process. Here’s a brief on each:
Account-level webhooks
Set up through the dashboard’s webhooks tab, these webhooks notify you of events related to any agent under your account. Specify your webhook URL in the dashboard’s webhooks tab to activate.
Agent-level webhooks
When you create the agent, you can set webhook_url field.
Any event associated with that agent will be pushed to the agent webhooks url. If set, account level webhooks url will not be triggered for that agent.
Handle Webhook
After registering the webhook, you would want to verify the webhook is from Retell and handle it.
Webhook Payload
The webhook will be POST to the URL you provided with a JSON payload.
The payload will contain the event type and the call detail associated with the event.
It will contain a x-retell-signature header to help you verify the webhook comes from Retell.
Sample Payload
The call field in the webhook payload contains the call details. It’s the same content
you would receive when you fetch the call details using the get-call API.
{
"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": "...",
"transcript_object": [ [Object], [Object], [Object], [Object] ],
"transcript_with_tool_calls": [ [Object], [Object], [Object], [Object] ],
"opt_out_sensitive_data_storage": false
}
}
If metadata and retell_llm_dynamic_variables are not provided, they will be omitted from the webhook event payload.
For transcript_updated events, the payload also includes transcript_with_tool_calls. For transfer events, the payload includes transfer_destination and (when available) transfer_option.
Sample Transfer event payload
Here is a compact example of a transfer webhook payload (fields may vary by transfer type):
{
"event": "transfer_started",
"call": {
"call_id": "Jabr9TXYYJHfvl6Syypi88rdAHYHmcq6"
},
"transfer_destination": {
"number": "+12137771235",
"extension": "1234"
},
"transfer_option": {
"type": "warm_transfer",
"showTransfereeAsCaller": true,
"publicHandoffOption": {
"type": "static_message",
"message": "Hi, I am transferring the caller now."
},
"agentDetectionTimeoutMs": 15000,
"onHoldMusic": {
"type": "default"
},
"enableBridgeAudioCue": true
}
}
Verifying Webhook
You can use the x-retell-signature header together with your Retell API Key to verify the webhook.
We have provided
verify function in our SDKs to help you with this.
You can also check and allowlist Retell IP addresses: 100.20.5.228.
The following code snippets demonstrate how to verify and handle the webhook in Node.js and Python.
Sample Code
// install the sdk: https://docs.retellai.com/get-started/sdk
import { Retell } from "retell-sdk";
import express, { Request, Response } from "express";
const app = express();
app.use(express.json());
app.post("/webhook", (req: Request, res: Response) => {
if (
!Retell.verify(
JSON.stringify(req.body),
process.env.RETELL_API_KEY,
req.headers["x-retell-signature"] as string,
)
) {
console.error("Invalid signature");
return;
}
const {event, call} = req.body;
switch (event) {
case "call_started":
console.log("Call started event received", call.call_id);
break;
case "call_ended":
console.log("Call ended event received", call.call_id);
break;
case "call_analyzed":
console.log("Call analyzed event received", call.call_id);
break;
case "transcript_updated":
console.log("Transcript updated event received", call.call_id);
break;
case "transfer_started":
case "transfer_bridged":
case "transfer_cancelled":
case "transfer_ended":
console.log("Transfer event received", event, call.call_id);
break;
default:
console.log("Received an unknown event:", event);
}
// Acknowledge the receipt of the event
res.status(204).send();
});
Testing Locally
To test webhooks on your local machine, you can use ngrok
to generate a production url forwarding requests to your local endpoints.
Privacy
Choosing to “Opt-Out of Personal and Sensitive Data Storage” means transcripts and recordings post-call won’t be stored.
However, transcripts and recording remain accessible via webhooks, allowing for alternative storage
solutions on your end.
The recording will also be available in the recording_url field.
The link will be accessible for 10 minutes and will be
deleted and become inaccessible after 10 minutes.
Response
We expect a successful status code (2xx) to be returned. No body is expected.
Video Tutorial
See community templates in docs