Skip to main content

Overview

Webhooks allow you to receive HTTP POST notifications when events occur in your Voicy account. Currently, the call_ended event is supported.

Configuration

Configure webhooks in the Voicy Dashboard under Agent Settings > Webhook.
  • URL — The HTTPS endpoint that will receive webhook POST requests
  • Signing Secret (optional) — Used to verify webhook authenticity via HMAC-SHA256

Events

call_ended

Fired after all post-call processing completes (summary generation, variable extraction, email notifications). The payload contains the full call data in the same format as the Get Call API. Headers:
HeaderDescription
Content-Typeapplication/json
x-voicy-eventEvent name (call_ended)
x-voicy-signatureHMAC-SHA256 signature (only if signing secret is configured)
Payload:
{
  "event": "call_ended",
  "call": {
    "call_id": "550e8400-e29b-41d4-a716-446655440000",
    "call_type": "phone_call",
    "call_status": "user_hangup",
    "agent": {
      "id": "agent-uuid",
      "name": "My Agent",
      "version": 3
    },
    "from_number": "+972501234567",
    "to_number": "+972521234567",
    "direction": "incoming",
    "start_timestamp": 1710000000000,
    "duration_ms": 45000,
    "transcript": [
      { "role": "agent", "content": "שלום, איך אוכל לעזור?" },
      { "role": "user", "content": "אני צריך עזרה" }
    ],
    "summary": "הלקוח ביקש עזרה בנושא...",
    "vars_provided": { "from_number": "+972501234567" },
    "vars_extracted": { "customer_name": "משה" },
    "vars_collected": { "email": "user@example.com" },
    "call_cost": { "combined_cost": 0.0234, "currency": "usd" }
  }
}

Signature Verification

If a signing secret is configured, each webhook request includes an x-voicy-signature header containing an HMAC-SHA256 signature of the request body.

Node.js

const crypto = require('crypto');

function verifyWebhook(body, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Express middleware
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-voicy-signature'];
  if (!verifyWebhook(req.body, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  const event = JSON.parse(req.body);
  console.log('Call ended:', event.call.call_id);
  res.status(200).send('OK');
});

Python

import hmac
import hashlib

def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
    expected = 'sha256=' + hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

Behavior

  • Method: POST
  • Timeout: 5 seconds
  • Retries: None (fire-and-forget)
  • Content-Type: application/json
If the webhook endpoint is unreachable or returns an error, the failure is logged but does not affect call processing.