By Sagar Shankaran, Founder of CallSphere
Production webhook patterns for AI voice agents — idempotency keys, retry strategies, signature verification, and observability.
Key takeaways
Voice agents are bidirectional: incoming webhooks from Twilio, Stripe, calendar systems, CRMs, SMS gateways; outgoing webhooks to customer integrations. Every single one is a place where a message can be delivered twice, out of order, or never. Get the webhook layer right and the rest of your platform gets quiet. Get it wrong and you will spend weekends debugging "why did we charge the customer three times?"
This post is a field guide to the webhook patterns that actually work in production for AI voice agents.
sender → https://webhooks.yourapp.com/source/v1
│
│ HMAC verify
▼
idempotency lookup (Redis)
│
├── hit → return cached response
│
▼
enqueue for worker
│
▼
worker processes → writes status + response
┌───────────┐ HTTPS ┌─────────────────┐
│ Twilio │──────► │ Ingest service │
│ Stripe │ │ (FastAPI) │
│ Calendar │ │ • HMAC verify │
│ HubSpot │ │ • idempotency │
└───────────┘ │ • enqueue │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Redis / SQS │
└────────┬────────┘
▼
┌─────────────────┐
│ Worker pool │
└─────────────────┘
Never process a webhook before verifying the HMAC. Every provider does this slightly differently; centralize the verification logic.
flowchart TD
CALL(["Inbound Call"])
HEALTH{"Primary<br/>agent healthy?"}
PRIMARY["Primary agent<br/>LLM provider A"]
SECONDARY["Hot standby<br/>LLM provider B"]
QUEUE[("Persisted<br/>call state")]
HUMAN(["Live human<br/>fallback"])
DONE(["Caller served"])
CALL --> HEALTH
HEALTH -->|Yes| PRIMARY
HEALTH -->|Timeout or 5xx| SECONDARY
PRIMARY --> QUEUE
SECONDARY --> QUEUE
PRIMARY --> DONE
SECONDARY --> DONE
SECONDARY -->|Both fail| HUMAN
style HEALTH fill:#f59e0b,stroke:#d97706,color:#1f2937
style PRIMARY fill:#4f46e5,stroke:#4338ca,color:#fff
style SECONDARY fill:#0ea5e9,stroke:#0369a1,color:#fff
style HUMAN fill:#dc2626,stroke:#b91c1c,color:#fff
style DONE fill:#059669,stroke:#047857,color:#fff
import hmac, hashlib, base64
from fastapi import Request, HTTPException
def verify_twilio(req_body: bytes, signature: str, url: str, auth_token: str) -> bool:
data = url + req_body.decode()
mac = hmac.new(auth_token.encode(), data.encode(), hashlib.sha1).digest()
expected = base64.b64encode(mac).decode()
return hmac.compare_digest(expected, signature)
async def handle(req: Request):
body = await req.body()
sig = req.headers.get("X-Twilio-Signature", "")
if not verify_twilio(body, sig, str(req.url), AUTH_TOKEN):
raise HTTPException(401, "bad signature")
Use the provider's event ID as the dedupe key. Store the result in Redis with a TTL longer than the provider's retry window.
import redis.asyncio as redis
r = redis.from_url("redis://cache:6379/0")
async def dedupe(event_id: str) -> bool:
# returns True if first time, False if duplicate
set_ok = await r.set(f"wh:{event_id}", "1", nx=True, ex=86400)
return bool(set_ok)
Webhook senders will retry on anything other than 2xx. Do the minimum work synchronously and push the rest to a queue.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
from fastapi import Response
async def handle(req: Request):
body = await req.body()
# ... verify + dedupe ...
await queue.publish("webhook_events", body)
return Response(status_code=204)
Workers should retry with exponential backoff and route permanent failures to a dead-letter queue.
async function processEvent(msg: Buffer, attempt = 0) {
try {
const evt = JSON.parse(msg.toString());
await dispatch(evt);
} catch (err) {
if (attempt < 5) {
const delay = Math.min(30000, Math.pow(2, attempt) * 1000);
setTimeout(() => processEvent(msg, attempt + 1), delay);
} else {
await dlq.send(msg);
}
}
}
When your voice agent fires webhooks to customer systems, follow the same rules in reverse: sign the payload, retry on 5xx, honor Retry-After, and expose a replay API.
import httpx, uuid
async def deliver(url: str, event: dict, secret: str):
payload = json.dumps(event, sort_keys=True)
sig = hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()
headers = {
"Content-Type": "application/json",
"X-CallSphere-Signature": "sha256=" + sig,
"X-CallSphere-Event-Id": str(uuid.uuid4()),
}
async with httpx.AsyncClient(timeout=10) as c:
return await c.post(url, content=payload, headers=headers)
Full audit trail: event ID, source, payload hash, verification result, processing result, retry count.
Retry-After.CallSphere's webhook layer sits in front of the voice agent edge and handles Twilio call status, Stripe payments, Google Calendar push notifications, HubSpot deal updates, and custom customer webhooks for IT helpdesk ticketing. Every inbound event is HMAC-verified, deduplicated in Redis, and enqueued to a worker pool. Outbound webhooks fire for post-call events so customers can sync CallSphere data into their own CRMs and data warehouses.
The voice plane itself runs on the OpenAI Realtime API with gpt-4o-realtime-preview-2025-06-03, PCM16 at 24kHz, and server VAD. Post-call analytics from a GPT-4o-mini pipeline are also delivered via outbound webhooks with the same idempotency and signature patterns. Across 14 healthcare tools, 10 real estate agents, 4 salon agents, 7 after-hours escalation tools, 10-plus-RAG IT helpdesk tools, and the 5-specialist ElevenLabs sales pod, the webhook discipline is the same.
At least as long as the provider's retry window — 24h is a safe default.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
Yes, but a unique index on the event ID column is essential.
204 is more correct for "no body", but 200 is universally accepted.
Keep a recorded request fixture per provider and assert verification passes and fails correctly.
Require mTLS, source IP allowlisting, or a shared secret in the URL path as a fallback.
Want to see a production webhook pipeline in action? Book a demo, read the platform page, or see pricing.
#CallSphere #Webhooks #Idempotency #Reliability #VoiceAI #APIs #AIVoiceAgents
Written by
Sagar Shankaran· Founder, CallSphere
Sagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
A how-to for Colombian education and tutoring SMBs to answer parents and students instantly, book trial classes 24/7 in Spanish and English, and grow enrollment with a CallSphere AI agent.
Tbilisi professional-services firms serving relocating founders and IT companies use CallSphere AI voice and chat agents to answer enquiries 24/7 in English, Georgian and Russian and book consultations.
A practical how-to for Palau eco-resorts and dive operators on capturing every high-value, multilingual enquiry with a CallSphere AI voice and chat agent, while honouring Palau’s marine-conservation commitments.
How salons, spas and wellness SMBs across the UAE, Saudi Arabia and Qatar use CallSphere AI voice and chat agents to capture every booking 24/7 in Arabic, English and expat languages, and cut no-shows.
How estate agents and property managers in Luxembourg City and across the Grand Duchy use CallSphere to capture multilingual viewing and enquiry calls 24/7, GDPR compliant.
A practical guide for Senegalese logistics, freight, legal and consulting SMBs in Dakar and Thiès to capture every enquiry 24/7 with a CallSphere AI voice and chat agent in French, Wolof and English.
© 2026 CallSphere LLC. All rights reserved.
Made within New York
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.
Try Live DemoBook a DemoCalculate Your ROI