Webhook Types
RevRing supports two types of webhooks:- Pre-Call Webhooks: Called before an inbound call is answered, allowing you to personalize the agent’s behavior
- Post-Call Webhooks: Called after any call ends, providing complete call details including transcript and recording
Pre-Call Webhooks
Pre-call webhooks enable dynamic customization of agent behavior based on caller information. Before answering an inbound call, RevRing sends caller details to your webhook and uses the response to personalize the conversation.When to Use
- Look up caller information in your CRM
- Customize greetings based on customer status or history
- Route calls differently based on caller context
- Apply business hours logic or special handling
- Personalize the agent’s knowledge with customer-specific data
Configuration
- Navigate to your agent’s Advanced tab
- Set the Pre-call Webhook URL:
- Click Save
Pre-call webhooks only apply to inbound calls. Outbound calls receive variables directly via the API when the call is sent.
Request Format
When an inbound call arrives, RevRing sends a POST request to your webhook:event: Always"pre_call"for pre-call webhooksagentId: The agent that will handle the callfromNumber: Caller’s phone number (E.164 format)toNumber: The number that was dialeddialedAgentId: The agent ID when the call was routed directly to an agent (null if routed via phone number)callback: Present only when the caller is returning a call your account placed in the callback window:{ "callId", "calledAt", "outcome" }(the outbound call, when it was placed, andanswered,voicemail,no_answer,busyordeclined). See Callbacks
Response Format
Your webhook must respond within 5 seconds (aim for well under one: the caller is waiting in silence) with a JSON object containing variables:{{variable_name}} syntax.
Variables you return can override system variables like
{{current_time}} and {{timezone}} if needed. However, system variables ({{direction}}, {{user_number}}, {{agent_number}}, {{current_time}}, {{timezone}}, {{language}}) are automatically provided and don’t need to be set via webhook.Route, personalize or reject the call
The response can also decide how the call runs. Every field is optional:
If the agent or environment you name does not exist, the call runs on the dialed agent as usual, so a mistake in your webhook never drops a caller.
Moving from another platform? A Retell inbound webhook answer (
{ "call_inbound": { "override_agent_id", "dynamic_variables" } }) and an ElevenLabs conversation initiation answer (dynamic_variables, conversation_config_override.agent.first_message and .prompt.prompt) work as they are. A Retell agent id resolves to the agent you imported from it.Using Variables in Agent Configuration
Reference webhook variables in your First Message:Error Handling
If your webhook fails, times out, or returns invalid JSON:- RevRing will proceed with the call using default variables (if configured)
- The call will not be rejected - the agent will answer without personalization
- Check the call’s
preCallWebhookStatusfield to see webhook delivery status
sent: Webhook responded successfullyfailed: Webhook returned an error or timed outskipped: No webhook URL configurednull: Call has not yet reached the webhook stage
Example Implementation
Node.js / Express:Post-Call Webhooks
Post-call webhooks deliver complete call details immediately after a call ends, including the transcript, recording URL, summary, and metadata.When to Use
- Update your CRM with call outcomes
- Log call details for compliance or auditing
- Trigger follow-up workflows (emails, tasks, tickets)
- Track agent performance metrics
- Store transcripts and recordings in your system
- Implement custom analytics or reporting
Configuration
- Navigate to your agent’s Advanced tab
- Set the Post-call Webhook URL:
- Click Save
Post-call webhooks fire for both inbound and outbound calls.
Request Format
After a call ends, RevRing sends a POST request with complete call details:id: Unique call identifierorganizationId: Your organization IDagentId: The agent that handled the callagentName: The agent’s display namesipTrunkId: SIP trunk used for the call (null if not applicable)direction:INBOUNDorOUTBOUNDstatus: Final call status (COMPLETED,TRANSFERRED,FAILED,CANCELED)fromNumber/toNumber: Caller and recipient numbers (E.164)agentCallerIdName: Caller ID name configured on the agent (null if not set)variables: All template variables used during the call — includes variables from pre-call webhooks, API call variables, and system variables (direction,current_time,timezone, etc.)recordingUrl: URL to download the call recording (null if recording disabled or call not answered)hangupCause: Reason call ended. Common values includeUSER_ENDED,END_CALL_TOOL,SILENCE_TIMEOUT,MAX_DURATION,VOICEMAIL_DETECTEDtranscript: Complete conversation with timestamps and tool invocations (both custom tools and system tools likeend_call,transfer_to_number)summary: AI-generated summary of the callextraction: Structured fields pulled from the transcript, as configured in the agent’spostCallExtraction(see Post-call data extraction);nullwhen not configuredevaluation: Pass/fail/unknown verdicts for the agent’spostCallEvaluationcriteria, each with a rationale (see Post-call evaluation);nullwhen not configuredcampaignId: The campaign that placed the call;nullfor calls not placed by a campaignmetrics: Token usage, audio durations and response latency (see Response latency)durationSeconds: Total call duration (null if never answered)
The example above shows both a custom tool (
check_order_status) and a system tool (end_call). When a call is transferred using transfer_to_number, the transcript will contain a similar tool entry with name: "transfer_to_number" and args containing to_number.Response latency
metrics.latency shows how quickly the agent answered on this call, measured per agent turn and summarized as count, p50_ms, p90_ms and max_ms (milliseconds):
Interrupted or discarded replies are not counted, and neither are acknowledgments the caller says while the agent is talking. Calls where the caller never spoke have
count: 0. stop_to_audio and stop_to_eot are present on calls from October 10, 2026.
Timing on each agent turn
Agent turns in a call’s transcript carry a latency object with how long that reply took, in milliseconds. Only measured values are included.
GET /v1/analytics/reply-times?agentId=).
Tool timing fields
Custom tool entries carry how long your webhook took to respond, so you can measure tool latency yourself from the transcript, with no need to ask support:
The same timing is shown on each tool card in the call transcript in the dashboard.
For complete field descriptions and additional examples, see the Post-Call Webhook API Reference.
Post-call data extraction
Define the fields you want pulled out of every conversation and the platform fills them in from the transcript after each call. The result lands on the call record asextraction (GET /calls/{id}) and in the post-call webhook, so your systems get typed values instead of parsing the summary.
Configure it on the agent with postCallExtraction:
How it behaves:
- Up to 20 fields per agent. Set
postCallExtractiontonullto turn it off. - Values come only from what was said on the call. A field that was not stated, or was unclear, is
null, never guessed. - Every configured field is present in the result, so your handler can rely on the keys.
- Extraction runs after the call ends, alongside the summary, and is skipped for calls with no caller speech (unanswered dials, dead lines). Those calls carry
extraction: null. - The result is included in the same post-call webhook delivery. No extra request to make.
Post-call evaluation
Write the outcomes you care about in plain language and every call is judged against them after it ends. Each criterion comes back aspass, fail or unknown with a one-sentence rationale, on the call record as evaluation (GET /calls/{id}) and in the post-call webhook. Use it for success rates, QA sampling and routing follow-ups.
Configure it on the agent with postCallEvaluation:
- Up to 10 criteria per agent. Set
postCallEvaluationtonullto turn it off. - A criterion that asks the agent to do something is
failwhen the call reached that point and the agent did not do it: a skipped required action counts against the agent. - A criterion about an outcome that depends on the caller (they book, buy, pay or share information) is
failwhen the call got to that topic and it did not happen, andunknownwhen the call never got there. unknownalso covers calls that ended before the agent could respond, so your success rate reflects what actually happened rather than guesses.- Every criterion is present in the result, so your handler can rely on the keys.
- Runs after the call ends, alongside the summary and extraction, and is skipped for calls with no caller speech. Those calls carry
evaluation: null. - Included in the same post-call webhook delivery.
evaluation) and on the dashboard’s Analytics page, so success rates need no spreadsheet.
Caller sentiment
Every call with caller speech gets asentiment: how the caller felt over the call as a whole, positive, neutral, negative or unknown (the caller barely spoke or the call ended too soon to tell). It is on the call record (GET /calls/{id}), in the post-call webhook, and in the call-history export. Calls with no caller speech carry sentiment: null. With the Retell compatibility format, it fills call_analysis.user_sentiment (an imported user_sentiment extraction field takes precedence).
Re-run analysis on past calls
Changed an agent’s extraction fields or success criteria? Re-run extraction, evaluation and sentiment on past calls with the agent’s current settings, without placing new calls:Export call history
GET /v1/calls/export returns a CSV of calls, newest first (up to 10,000 per file), filtered by agentId, status, direction, from and to. Columns: call details, sentiment, summary, recordingUrl, one var.* column per call variable, one extraction.* column per extracted field, and one evaluation.* column per success criterion (its result). In the dashboard, use Export CSV on Calls. Cells that would run as a spreadsheet formula are prefixed with an apostrophe.
Call started event
To also hear when a call connects, set the agent’swebhookEvents to ["call.started"]. The post-call webhook URL then receives, once per answered call:
event field, so a handler can tell the two apart. With the Retell compatibility format the event is Retell’s call_started. Started events are sent once and are not retried.
Transfer events
To hear about transfers as they happen, add any oftransfer.started, transfer.bridged and transfer.cancelled to the agent’s webhookEvents (or turn on Also send transfer events in the agent’s Advanced tab). The post-call webhook URL then receives:
transfer_started, transfer_bridged and transfer_cancelled. Transfer events are sent once and are not retried; the post-call webhook still arrives when the call ends.
Compatibility formats
Moving from another voice platform? Set the agent’spostCallWebhookFormat and the post-call webhook is sent in that platform’s payload shape, so the handler you already run keeps working while you switch. Agents created by the importer get this set for you.
The values come from the call record: extraction fields become Retell’s
custom_analysis_data or ElevenLabs’ data_collection_results, evaluation criteria become call_successful (and ElevenLabs’ evaluation_criteria_results), and an extraction field named user_sentiment fills Retell’s user_sentiment. Fields that only exist on the other platform, such as costs or per-word timings, are not sent.
Deliveries are signed with RevRing’s X-RevRing-Signature header, not the other platform’s signature, so replace your signature check (see Verify webhook sources). Retries work as described below.
Response Expectations
Your webhook should:- Respond with
200 OKstatus code - Respond within 30 seconds (a few seconds is better: acknowledge first, then process)
- Handle the webhook asynchronously if processing takes time
id: a retry carries the same payload, and a slow handler that returned after our 30 second timeout may be retried even though it processed the call. The call record shows the outcome in postCallWebhookStatus (sent, retrying, failed) with postCallWebhookAttempts.
Error Handling
If your webhook fails (non-2xx status), can’t be reached, or times out:- The delivery is retried automatically 1, 5 and 30 minutes later (see Retries above);
postCallWebhookStatusisretryingmeanwhile - After the last retry it is marked
failedin the call record - To backfill anything that ended
failed(for example after a long outage on your side), list recent calls withGET /callsand checkpostCallWebhookStatus
sent: Delivered successfullyfailed: Webhook returned an error or timed outskipped: No webhook URL configurednull: Call has not yet reached the webhook stage
Example Implementation
Node.js / Express:Security Best Practices
Verify Webhook Sources
Signature verification (recommended): Turn on webhook signing for your organization and verify every request. One secret signs every webhook RevRing sends you: pre-call, post-call and message received.- Create a secret (the full value is returned only by this call; store it with your other secrets):
- From then on each webhook carries two headers:
- Verify on the raw request body, before parsing JSON, and reject timestamps older than a few minutes:
POST (the new secret is live within a minute; verify against both during the switch), check the state with GET, and turn signing off with DELETE. See the signing secret reference.
Request Validation:
Implement validation to ensure webhook requests contain expected fields and structure.
Use HTTPS
Always use HTTPS for webhook URLs to ensure data is encrypted in transit:Validate Payload
Validate incoming webhook data before processing:Rate Limiting
Implement rate limiting to prevent abuse:Testing Webhooks
Local Development with ngrok
For local testing, use ngrok to expose your local server:Testing Tools
Request Inspection: Use webhook.site to inspect webhook payloads without writing code. Manual Testing: Use curl to simulate webhook responses:Test with Real Calls
- Configure your webhook URL in the Advanced tab
- For pre-call webhooks: use the test panel to make a browser call and check your server logs
- For post-call webhooks: use either the test panel or the Make a Call page
- Check call details in Calls to verify webhook status
Monitoring Webhooks
Check Webhook Status
For each call in Calls:- Click on the call to view details
- Check Webhook Status in the Overview card:
- Pre-call webhook status
- Post-call webhook status
- If failed, review your server logs for errors
Common Issues
Webhook returns timeout
Webhook returns timeout
- Ensure your webhook responds within 5 seconds (pre-call; the caller is waiting) or 30 seconds (post-call)
- Move slow operations (database writes, external API calls) to async background processing
- Return acknowledgment immediately, then process in background
- Check your server’s response time and optimize slow queries
Webhook marked as failed
Webhook marked as failed
- Verify your endpoint is accessible from the internet
- Check that you’re returning a 200 status code
- Ensure response is valid JSON (even if empty:
{}) - Review server logs for exceptions or errors
- Test endpoint with curl or Postman to verify it works
Variables not appearing in agent responses
Variables not appearing in agent responses
- Verify your pre-call webhook returns valid JSON with variable keys
- Check variable names match exactly between webhook and prompt (case-sensitive)
- Ensure the webhook responds within 5 seconds; after that the call starts without its variables
- Test webhook independently to confirm it returns expected data
- Review the call’s
variablesfield in call logs to see what was actually received
Not receiving post-call webhooks
Not receiving post-call webhooks
- Confirm the webhook URL is configured in the agent’s Advanced tab
- Verify the URL is correct and accessible
- Check your server logs - RevRing may be sending but your server isn’t logging
- Test with webhook.site to confirm RevRing is sending requests
- Review firewall rules that might block incoming requests
Best Practices
Performance
- Respond quickly: Acknowledge webhooks within 1-2 seconds
- Process asynchronously: Handle heavy processing in background jobs
- Cache frequently: Cache CRM lookups or database queries when possible
- Use connection pooling: Reuse database connections across requests
- Set timeouts: Configure timeouts for external API calls to prevent hanging
Reliability
- Idempotency: Design webhooks to handle duplicate calls (use
idas idempotency key) - Error handling: Wrap processing in try/catch and return 200 even if internal processing fails
- Logging: Log all webhook requests and responses for debugging
- Monitoring: Set up alerts for webhook failures or high error rates
- Fallbacks: Provide default values if lookups fail
Security
- HTTPS only: Never use HTTP for webhook URLs
- Validate input: Sanitize and validate all incoming data
- Request validation: Verify requests contain expected structure and fields
- Rate limiting: Protect against potential abuse
- Audit logging: Log all webhook activity for security review
Advanced Patterns
Dynamic Agent Customization
Use pre-call webhooks to provide variables that customize agent behavior for each caller: Webhook Response:Call Outcome Tracking
Use post-call webhooks to track outcomes and trigger workflows:Inbound Message Webhook
When an agent’s inbound messaging mode is set to notify, RevRing forwards each incoming text to a webhook URL you choose, and your own system composes and sends the reply. (In auto-reply mode RevRing replies automatically from the agent’s SMS prompt, and no webhook is sent — see Two-way texting.)When to Use
- You orchestrate replies yourself (in N8N, Make, your CRM, etc.) and want full control over each reply.
- You want to log, score, or branch on an inbound text before responding.
Configuration
- On the agent’s Messaging tab, set When a text comes in to Notify my webhook and enter your webhook URL.
- Assign the receiving number to the agent under Numbers.
- Point your provider’s inbound message webhook at RevRing:
Request Format
When an inbound text arrives, RevRing sends a POST request to your webhook URL:event: Always"message_received"for inbound messages.organizationId: Your organization ID.agentId: The agent that owns the number that received the text.conversationId: RevRing’s id for the thread with this contact. Use it with the messages log to read the full conversation.fromNumber: The contact’s number (E.164).toNumber: Your number that received the text (E.164).body: The text content of the incoming message.messageSid: Your provider’s id for the inbound message.
Response Expectations
Respond with200 OK promptly. RevRing does not require a response body and does not retry. To reply, call the Send Message API.
Example Implementation
Node.js / Express:Next Steps
Tools
Configure system and custom tools
API Reference
View complete webhook API documentation
Inbound Calls
Set up inbound call handling
Outbound Calls
Configure outbound calling