Webhooks

Receive real-time HTTP callbacks when calls are queued, finish, or are analyzed. Every payload is signed with HMAC-SHA256 in the X-Voice-Signature header.

Configuration

Set a default webhook URL for your organization:

  • Dashboard: Settings → Webhook URL → Save
  • API: PATCH /dashboard/webhook with {"webhook_url":"https://yourapp.com/voice/hooks"}

Override per call with webhook_url on POST /v1/calls/outbound — useful for multi-tenant SaaS routing events to the correct subscriber endpoint.

Each organization receives a unique webhook_secret at signup (stored hashed server-side). Use this secret to verify signatures — never expose it in client-side code.

Event types

EventWhen firedTypical payload fields
call.queued Immediately after outbound call is persisted to, status, metadata
call.ended Call completes or fails (Phase 2 worker) status, duration_seconds, disposition
call.analyzed Transcript and structured answers available transcript, structured_answers, recording_url

Envelope format

All events share a JSON envelope:

{ "event": "call.queued", "org_id": "550e8400-e29b-41d4-a716-446655440000", "call_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "timestamp": "2026-06-01T14:30:00.123456+00:00", "to": "+15551234567", "status": "queued", "metadata": { "source": "crm" } }

Additional fields are merged at the top level per event type. Timestamps are ISO 8601 UTC.

X-Voice-Signature

VoxBridge signs the raw JSON body with your organization's webhook secret:

X-Voice-Signature: sha256=<hex_hmac_sha256>

Verification algorithm:

  1. Read the raw request body bytes (before JSON parsing).
  2. Compute HMAC-SHA256(secret, body) as lowercase hex.
  3. Compare to header value sha256={digest} using a constant-time comparison.
# Python example import hmac, hashlib def verify(secret: str, body: bytes, header: str) -> bool: expected = "sha256=" + hmac.new( secret.encode(), body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected, header)

Delivery behavior

  • HTTP POST with Content-Type: application/json
  • 15-second timeout per delivery attempt
  • Success = HTTP 2xx response from your server
  • Events are logged in webhook_events with delivered flag and response code for dashboard debugging

Implement idempotent handlers keyed on call_id + event — retries may occur if your endpoint is slow or returns errors.

Example handler (Node.js)

app.post("/voice/hooks", express.raw({ type: "application/json" }), (req, res) => { const sig = req.headers["x-voice-signature"]; if (!verify(process.env.VOICE_WEBHOOK_SECRET, req.body, sig)) { return res.status(401).send("invalid signature"); } const payload = JSON.parse(req.body.toString()); if (payload.event === "call.analyzed") { // Update CRM with transcript } res.sendStatus(200); });

Related

Calls API → · Quick start →