Webhooks deliver real-time event notifications to your server via HTTP POST. Instead of polling the API, your server receives events the moment they occur — call ended, recording ready, analysis complete.
How Webhooks Work
An event occurs in Neuratel (e.g., a call ends)
Neuratel sends an HTTP POST to your configured URL
Your server verifies the signature and returns HTTP 200
If delivery fails, Neuratel retries with exponential backoff
Event Types
Event
When It Fires
call.started
When a call begins
call.ended
Immediately when a call ends
call.ringing
Outbound call is ringing
call.answered
Call is answered
call.failed
When an error occurs during a call
call.transferred
Call transferred to another destination
call.summary.ready
When post-call analysis (summary, transcript, evaluation) is complete. Includes recording_url if recording is enabled.
recording.ready
Coming soon — defined in the API but not yet emitted. Recording URLs are surfaced on call.summary.ready and on the voice session record.
Quick Start
1. Create a Webhook Endpoint
Your endpoint receives POST requests and must return 200.
Code
from fastapi import FastAPI, Request, HTTPExceptionimport hmacimport hashlibapp = FastAPI()WEBHOOK_SECRET = "whsec_your_signing_secret"@app.post("/webhooks/neuratel")async def receive_webhook(request: Request): body = await request.body() signature = request.headers.get("X-Webhook-Signature") if not signature or not verify_signature(body, signature, WEBHOOK_SECRET): raise HTTPException(status_code=401, detail="Invalid signature") payload = await request.json() event_type = payload["meta"]["event_type"] match event_type: case "call.summary.ready": # Includes recording_url if recording was enabled on the agent. await handle_call_summary(payload["data"]) case "call.ended": await handle_call_ended(payload["data"]) return {"status": "ok"}def verify_signature(body: bytes, signature: str, secret: str) -> bool: expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, signature)
recording.ready (coming soon — not yet emitted in production)
This event is defined in the API enum but not yet emitted by the platform. Subscribing to it has no effect today. For now, retrieve the recording URL from the call.summary.ready event payload, or fetch it on demand via GET /v1/voice-sessions/{id} (the response includes recording.recording_url when recording was enabled).
Preview of the payload shape (subject to change before launch):
If your endpoint does not return a 2xx status within the timeout window, Neuratel retries:
Attempt
Delay
1
5 seconds
2
30 seconds
3
2 minutes
After 3 failed attempts, the delivery is marked failed. Webhooks are automatically disabled after 10 consecutive delivery failures — re-enable via the dashboard or PATCH /v1/webhooks/{id} with is_active: true.
Neuratel guarantees at-least-once delivery. The same event may arrive more than once. Use meta.event_id to deduplicate:
Code
processed_ids: set[str] = set() # use a persistent store in productionasync def process_event(payload: dict): event_id = payload["meta"]["event_id"] if event_id in processed_ids: return processed_ids.add(event_id) # process...
Verify Every Request
Always verify the X-Webhook-Signature header before processing any payload. Do not trust requests without a valid signature.