Webhook Events
Agency plan requiredWebhooks let your server receive real-time notifications when call events occur. Configure a webhook URL and BuildVoiceAI will send POST requests to your endpoint whenever a call starts, completes, or fails.
Configuration
Setting Up a Webhook URL
- Navigate to Integrations → API & Webhooks
- Enter your webhook endpoint URL in the Webhook URL field
- Click Save
Your endpoint must:
- Be publicly accessible (not behind a firewall or VPN)
- Accept POST requests with a JSON body
- Return a
200status code within 10 seconds - Use HTTPS (HTTP endpoints are not supported)
Event Types
BuildVoiceAI sends the following event types:
| Event | Fired When |
|---|---|
call.started | A call is initiated and the connection is established |
call.completed | A call ends normally |
call.failed | A call fails due to an error (provider issue, invalid number, etc.) |
Payload Format
All webhook payloads follow this structure:
your-webhook-urlBuildVoiceAI sends this payload to your configured endpoint.
call.completed
{
"event": "call.completed",
"call_id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210",
"agent_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"agent_name": "Sales Qualifier",
"direction": "outbound",
"phone_number": "+15551234567",
"duration_seconds": 185,
"started_at": "2025-06-15T14:30:00Z",
"ended_at": "2025-06-15T14:33:05Z",
"sentiment": "positive",
"summary": "Caller expressed interest in the enterprise plan and requested a follow-up demo.",
"transcript": [
{
"role": "agent",
"content": "Hello, how can I help you today?",
"timestamp": "2025-06-15T14:30:01Z"
},
{
"role": "human",
"content": "I was wondering about your pricing.",
"timestamp": "2025-06-15T14:30:05Z"
}
],
"metadata": {
"source": "hubspot",
"deal_id": "deal_12345"
}
}call.started
{
"event": "call.started",
"call_id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210",
"agent_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"agent_name": "Sales Qualifier",
"direction": "outbound",
"phone_number": "+15551234567",
"started_at": "2025-06-15T14:30:00Z",
"metadata": {}
}call.failed
{
"event": "call.failed",
"call_id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210",
"agent_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"agent_name": "Sales Qualifier",
"direction": "outbound",
"phone_number": "+15551234567",
"error": "Phone number is not reachable",
"failed_at": "2025-06-15T14:30:03Z",
"metadata": {}
}Signature Verification
Every webhook request includes an X-Signature header containing an HMAC-SHA256 signature. Verify this to confirm the request came from BuildVoiceAI and was not tampered with.
Verification Steps
- Get the raw request body (do not parse it first)
- Compute HMAC-SHA256 using your API key as the secret
- Compare the result with the
X-Signatureheader value
Example (Node.js)
const crypto = require('crypto');
function verifyWebhookSignature(rawBody, signature, apiKey) {
const expected = crypto
.createHmac('sha256', apiKey)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Always use a timing-safe comparison function to prevent timing attacks. Do not use === for signature comparison.
Retry Policy
If your webhook endpoint returns a non-2xx status code or does not respond within 10 seconds, BuildVoiceAI retries the request:
| Attempt | Delay |
|---|---|
| 1st retry | 30 seconds after initial attempt |
| 2nd retry | 5 minutes after 1st retry |
| 3rd retry | 30 minutes after 2nd retry |
After 3 failed retries, the event is dropped. Failed deliveries are logged and visible on the API & Webhooks integration page.
Return a 200 response immediately and process the webhook payload asynchronously. This prevents timeouts if your processing logic takes time.
Testing Webhooks
To test your webhook integration during development:
- Use a service like webhook.site or ngrok to get a public URL
- Set that URL as your webhook URL in BuildVoiceAI
- Trigger a test call
- Inspect the received payload
Best Practices
- Respond quickly — Return
200within 10 seconds to avoid retries - Process asynchronously — Queue webhook payloads for background processing
- Verify signatures — Always validate the
X-Signatureheader in production - Handle duplicates — Use
call_idto deduplicate in case of retries - Log payloads — Store raw webhook payloads for debugging