Webhooks for call results: signatures, retries and idempotency
Every finished call can update your CRM, order system or sheet. Here is how to receive call webhooks safely and reliably.
The Vaakyo team2 min read
- Webhooks
- Engineering
A voice agent is only as useful as what happens after the call. Webhooks deliver each call's outcome to your systems the moment it ends: the status, duration, transcript, summary, extracted fields and cost.
Verify the signature
Anyone can send a POST to your endpoint, so check that each request really comes from Vaakyo. Every webhook carries an X-Voxa-Signature header of the form t=<unix seconds>,v1=<hex HMAC-SHA256>. The signature is an HMAC-SHA256 of <t>.<raw request body>, keyed with your webhook secret:
import hashlib, hmac, time
def verify(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
timestamp, signature = parts.get("t", ""), parts.get("v1", "")
if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > tolerance:
return False # too old: a replay
expected = hmac.new(secret.encode(), f"{timestamp}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
Compute it over the raw body exactly as received, before parsing JSON.
Answer fast, work later
Return a 2xx within a few seconds, then do the work in the background (a queue, a job). If your endpoint is slow or failing, Vaakyo retries, so a long-running handler risks receiving the same event again while it's still processing.
Expect duplicates
Retries mean you may receive an event more than once. Make processing idempotent: use the call id (and the event type) as a key, and skip work you've already done.
Choose the events you need
You don't need every event. For most integrations, the end-of-call event with the results is enough; add live events (call started, transfer, tool calls) only if you show them in real time somewhere.
Map results to actions
Decide in advance what each outcome does:
- A confirmed COD order is released for shipping.
- A booked appointment is written to the calendar and an SMS goes out.
- A hot lead is assigned to a salesperson.
- A failed call is retried, or flagged for a person.
Watch the delivery log
The console's webhook log shows every delivery with its response code and timing, and lets you resend one. When an integration breaks, start there.