VoiceDock Docs
Core Concepts

Webhooks

Webhooks allow you to receive real-time notifications about call events via HTTP POST requests to your configured endpoint.

Webhooks allow you to receive real-time notifications about call events. Configure a webhook URL on your assistant to receive HTTP POST requests when events occur.

Webhook Events

EventWhen TriggeredResponse Used
assistant-requestBefore call is answeredYes - can configure assistant
tool-callsAssistant needs to execute a toolYes - return tool result
status-updateCall status changesNo
end-of-call-reportAfter call endsNo

Configuring Webhooks

Set the webhook URL and events on your assistant:

curl -X PATCH https://api.hmsovereign.com/api/v1/assistants/ASSISTANT_ID 
  -H "Authorization: Bearer YOUR_API_KEY" 
  -H "Content-Type: application/json" 
  -d '{
    "webhook_url": "https://api.example.com/webhooks/hms-sovereign",
    "webhook_secret": "your-secret-for-verification",
    "webhook_events": ["assistant-request", "tool-calls", "status-update", "end-of-call-report"]
  }'

Webhook Headers

All webhook requests include these headers:

HeaderDescription
Content-Typeapplication/json
User-AssistantHMS-Sovereign/1.0
X-Webhook-EventEvent type (e.g., tool-calls)
X-Webhook-TimestampUnix timestamp of the request
X-Webhook-SignatureHMAC-SHA256 signature (if secret configured)

Signature Verification

If you configure a webhook_secret, verify the signature to ensure requests come from HMS Sovereign:

Python:

import hmac
import hashlib

def verify_signature(payload: str, secret: str, timestamp: str, signature: str) -> bool:
    sig_hex = signature.removeprefix("sha256=")
    message = f"{timestamp}.{payload}"
    expected = hmac.new(
        secret.encode(),
        message.encode(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(sig_hex, expected)

# In your webhook handler:
payload = request.body.decode()
timestamp = request.headers.get("X-Webhook-Timestamp")
signature = request.headers.get("X-Webhook-Signature")

if not verify_signature(payload, "your-secret", timestamp, signature):
    return Response("Invalid signature", status=401)

Node.js:

const crypto = require('crypto');

function verifySignature(payload, secret, timestamp, signature) {
  const sigHex = signature.replace(/^sha256=/, '');
  const message = `${timestamp}.${payload}`;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(message)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(sigHex),
    Buffer.from(expected)
  );
}

Event Details

Assistant Request

Called before an inbound call is answered. Allows you to:

  • Customize the assistant configuration per call
  • Personalize the greeting based on caller
  • Reject calls (e.g., outside business hours)

Timeout: 5 seconds. If your endpoint doesn't respond in time, the call proceeds with default configuration.

Response format:

{
  "assistant_id": "uuid",  // Reference mode: use saved assistant
  "assistant": { ... },    // Transient mode: full assistant config
  "assistant_override": { ... }  // Hybrid mode: merge with assistant_id
}

See Dynamic Assistant Configuration Webhook for payload details.

Tool Calls

Called when the assistant needs to execute a custom tool during the conversation.

Timeout: 10 seconds for synchronous tools.

Response formats supported:

  • Object format: {"results": [{"tool_call_id": "...", "result": {...}}]}
  • Simple format: {"result": {...}}
  • Direct format: {...} (your data directly)
  • Error format: {"error": "message"}

See Tool/Function Call Webhook for payload details.

Status Update

Called when the call status changes:

  • in-progress - Call connected
  • ended - Call ended normally
  • ended-with-error - Call ended due to error

Response is ignored.

See Call Status Update Webhook for payload details.

End of Call Report

Called after the call ends with:

  • Call duration
  • AI-generated summary
  • Structured analysis (if analysis_plan configured)

Response is ignored.

See End of Call Report Webhook for payload details.

Common Payload Fields

All webhooks include this structure:

{
  "message": {
    "type": "status-update",
    "timestamp": "2025-12-13T12:00:00.000Z",
    "call": {
      "id": "5c4d030f-43e3-4e65-899e-8148521e660f",
      "type": "inbound_phone_call",
      "status": "in-progress"
    },
    "phone_number": {
      "number": "+31850835037",
      "name": "HMS Sovereign Demo"
    },
    "customer": {
      "number": "+31612345678"
    },
    "assistant": {
      "id": "assistant-uuid",
      "name": "Customer Support Assistant",
      "llm_config": { ... },
      "tts_config": { ... },
      "stt_config": { ... }
    }
  }
}

Best Practices

  1. Respond quickly - Return 200 OK as fast as possible, especially for status updates
  2. Process asynchronously - Queue heavy processing for later
  3. Verify signatures - Always verify webhook signatures in production
  4. Handle retries - Implement idempotency for duplicate deliveries
  5. Log everything - Keep webhook logs for debugging

On this page