An After‑Call Webhook sends call details from your account to your server right after a call ends. It lets you automate CRM updates, analytics, quality checks, and alerts without polling.
Use an after‑call webhook when you need timely events for downstream systems.
Common use cases:
Tip: Use a separate test endpoint first. Promote to production after validation.
What you should see: Your endpoint returns HTTP 200 OK within 5 seconds. The app marks the webhook test as Delivered.
Alt text: After‑Call Webhook form with URL, Auth type, and Send Test button.Caption: Configure destination URL, auth, and payload format before sending a test.
Use one of the following methods.
Method A — Logs in your server
Method B — cURL mock
curl -X POST "https://example.com/webhooks/after-call" \
-H "Content-Type: application/json" \
-H "X-Webhook-Token: REPLACE_WITH_SECRET" \
-d '{
"event": "call.completed",
"call_id": "cl_8fKQ92",
"direction": "inbound",
"from": "+
1408555123
4",
"to": "+
1408555987
6",
"started_at": "2025-08-21T10:58:13Z",
"ended_at": "2025-08-21T11:02:44Z",
"duration_seconds": 271,
"status": "answered",
"agent": {"id": "ag_102", "name": "Jordan"},
"recording_url": "https://recordings.example/abc123.mp3",
"wrap_up_notes": "Callback scheduled"
}'
Method C — Postman
Alt text: Postman request showing POST to webhook URL with JSON body and 200 OK response.Caption: Use Postman to validate your endpoint and headers.
{
"event": "call.completed",
"event_id": "evt_5z7h3c",
"occurred_at": "2025-08-21T11:02:44Z",
"call_id": "cl_8fKQ92",
"parent_call_id": null,
"direction": "inbound",
"from": "+
1408555123
4",
"to": "+
1408555987
6",
"status": "answered",
"reason": null,
"queue": {"id": "q_23", "name": "Support"},
"agent": {"id": "ag_102", "name": "Jordan"},
"started_at": "2025-08-21T10:58:13Z",
"connected_at": "2025-08-21T10:58:33Z",
"ended_at": "2025-08-21T11:02:44Z",
"duration_seconds": 271,
"talk_time_seconds": 251,
"wrap_up_seconds": 20,
"recording_url": "https://recordings.example/abc123.mp3",
"notes": "Callback scheduled",
"custom_fields": {"crm_id": "ACME-4451", "priority": "high"}
}
Recommended options:
A) Custom header (preferred)
B) Basic Auth
Signature verification (optional hardening)
Server examples
PHP
<?php
$raw = file_get_contents('php://input');
$provided = $_SERVER['HTTP_X_WEBHOOK_TOKEN'] ?? '';
$secret = getenv('WEBHOOK_SECRET');
if (!hash_equals($secret, $provided)) { http_response_code(401); exit; }
// Optional HMAC
$hmac = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
if ($hmac && !hash_equals(hash_hmac('sha256', $raw, $secret), $hmac)) {
http_response_code(401); exit;
}
http_response_code(200);
echo 'ok';
Node.js (Express)
import express from 'express';
import crypto from 'crypto';
const app = express();
app.use(express.json({ type: '*/*' }));
app.post('/webhooks/after-call', (req, res) => {
const secret = process.env.WEBHOOK_SECRET;
const token = req.header('X-Webhook-Token');
if (token !== secret) return res.status(401).send('unauthorized');
const sig = req.header('X-Webhook-Signature');
if (sig) {
const h = crypto.createHmac('sha256', secret).update(JSON.stringify(req.body)).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(h))) {
return res.status(401).send('bad signature');
}
}
return res.status(200).send('ok');
});
app.listen(3000);
Python (Flask)
from flask import Flask, request, jsonify
import hmac, hashlib, os
app = Flask(__name__)
@app.post('/webhooks/after-call')
def after_call():
raw = request.get_data()
secret = os.getenv('WEBHOOK_SECRET','')
token = request.headers.get('X-Webhook-Token','')
if token != secret:
return 'unauthorized', 401
sig = request.headers.get('X-Webhook-Signature')
if sig:
h = hmac.new(secret.encode(), raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(h, sig):
return 'bad signature', 401
return jsonify({"status":"ok"}), 200
app.run()
Expected responses
200 OK # Processed
400 Bad Request # Malformed payload
401 Unauthorized # Token or signature failed
403 Forbidden # Auth absent or IP not allowed
410 Gone # Endpoint deprecated; stop sending
429 Too Many Requests # Back off and retry later
500/502/503 # Transient server error; we will retry
Prefer automation? Create or update the webhook via API.
Create webhook
curl -X POST https://api.example.com/v1/webhooks \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"type": "after_call",
"url": "https://example.com/webhooks/after-call",
"auth": {"header": "X-Webhook-Token", "secret": "<SECRET>"},
"events": ["answered","missed","voicemail"],
"format": "json"
}'
Rotate secret
curl -X PATCH https://api.example.com/v1/webhooks/wb_123 \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"auth": {"header": "X-Webhook-Token", "secret": "<NEW_SECRET>"}}'
After saving and testing:
To confirm success end‑to‑end:
No deliveries appear
401 Unauthorized
4xx client errors
5xx server errors or timeouts
Recording URL returns 403
Duplicate events
[Diagram — Recommended flow]Alt text: Sequence diagram showing Platform → Your Endpoint → Queue/Worker → CRM.Caption: Acknowledge fast, then process asynchronously.
What events does this webhook send?It sends call.completed for answered, missed, abandoned, and voicemail outcomes.
Can I filter which calls generate webhooks?Yes. Choose events in the webhook settings. You can also filter by queue or number via rules.
How do I secure recording access?Use expiring URLs or fetch recordings with an authenticated API call.
Do you sign payloads?You can enable HMAC signing and validate with X-Webhook-Signature.
What is the SLA for delivery?We attempt immediate delivery and retry on transient failures. Use logs in the app to review outcomes.