Webhooks
Receive real-time event notifications via webhooks.
Webhooks let your application receive real-time HTTP notifications when events happen in ErzyCall — such as a call ending, a contact being created, or a call failing.
How It Works
- You register a webhook endpoint URL in ErzyCall (via the API or dashboard).
- When a subscribed event occurs, ErzyCall sends a
POSTrequest to your URL with the event payload. - Your server verifies the signature and processes the event.
Available Events
| Event | Trigger |
|---|---|
call.started | An outbound call was initiated |
call.ended | A call completed |
call.failed | A call encountered an error |
call.scheduled | A call was scheduled for later |
call.cancelled | A scheduled call was cancelled |
contact.created | A new contact was created |
contact.updated | A contact was modified |
contact.deleted | A contact was deleted |
Webhook Payload
Each delivery is a POST request with a JSON body:
{
"event": "call.ended",
"timestamp": "2025-01-15T10:30:00.000Z",
"data": {
"id": "abc123",
"status": "ended",
"to": "+14155551234",
"from": "+14155559999",
"duration": 45
}
}Signature Verification
Every webhook delivery is signed with your endpoint's secret using HMAC-SHA256. Always verify the signature before processing the payload.
The signature is sent in the X-Webhook-Signature header:
X-Webhook-Signature: sha256=<hex-encoded HMAC>Verification Example (Node.js)
import crypto from "crypto";
function verifyWebhookSignature(payload, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
const expectedSig = `sha256=${expected}`;
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSig)
);
}
// In your webhook handler:
app.post("/webhook", (req, res) => {
const signature = req.headers["x-webhook-signature"];
const payload = JSON.stringify(req.body);
if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send("Invalid signature");
}
// Process the event
const { event, data } = req.body;
console.log(`Received ${event}:`, data);
res.status(200).send("OK");
});Managing Endpoints
Via the API
# Create a webhook endpoint
curl -X POST "https://app.erzycall.com/api/v1/webhooks" \
-H "X-API-Key: ek_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhook",
"events": ["call.ended", "contact.created"],
"description": "My production webhook"
}'The response includes a secret field — save this for signature verification:
{
"data": {
"id": "wh_abc123",
"url": "https://your-app.com/webhook",
"events": ["call.ended", "contact.created"],
"secret": "whsec_...",
"isActive": true,
"description": "My production webhook"
}
}Via the Dashboard
Navigate to Settings > API Keys > Webhooks tab to create and manage webhook endpoints through the UI.
Retry Behavior
If your endpoint returns a non-2xx status code or times out, ErzyCall retries the delivery up to 3 times with exponential backoff (delays of approximately 10s, 60s, and 300s).
Auto-Disable
If an endpoint fails 10 consecutive deliveries, it is automatically disabled. You can re-enable it from the dashboard or by updating the endpoint via the API:
curl -X PATCH "https://app.erzycall.com/api/v1/webhooks/{id}" \
-H "X-API-Key: ek_live_abc123" \
-H "Content-Type: application/json" \
-d '{"isActive": true}'Rotating Secrets
To rotate the signing secret for an endpoint:
curl -X POST "https://app.erzycall.com/api/v1/webhooks/{id}/rotate-secret" \
-H "X-API-Key: ek_live_abc123"The response contains the new secret. Update your verification code immediately — the old secret stops working right away.
Limits
- Maximum 5 webhook endpoints per organization.
- Maximum 10 events per endpoint.
- Webhook URLs must use HTTPS.
Best Practices
- Always verify signatures to ensure the payload came from ErzyCall.
- Respond quickly with a
200status. Process the event asynchronously if needed. - Handle duplicate deliveries — use the event data to deduplicate (e.g., by call ID).
- Monitor your endpoint — if deliveries start failing, check your logs before the endpoint gets auto-disabled.