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
LinkedInSagar 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 practical guide for medical clinics and aged-care providers in Sydney, Melbourne, Auckland, and Wellington to triage after-hours calls with AI voice agents under the Privacy Act.
A 2026 look at Austrian SMBs and the tourism-driven hospitality sector in Vienna, Graz and the alpine regions. How CallSphere AI voice and chat agents handle multilingual bookings 24/7, DSGVO-compliant, in German, English and more.
Azerbaijani law firms, accountants, and real estate agencies lose clients to unanswered calls. CallSphere books consultations 24/7 in Azerbaijani, Russian, and English across Baku and Ganja.
A practical guide for automotive businesses across the Balkans to deploy a CallSphere AI voice and chat agent that books service, answers parts questions, and captures every lead in every local language.
San Pedro on Ambergris Caye runs on divers and property buyers who inquire from every time zone. See how CallSphere AI voice + chat agents answer reef-trip bookings and real estate leads 24/7 in English and Spanish.
A pain-to-solution guide for logistics operators and professional-services firms across Poland, Czechia, Hungary and Slovakia to capture cross-border calls 24/7 with a CallSphere AI agent.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.
Try Live DemoBook a DemoCalculate Your ROI