API & Webhooks
The API & Webhooks integration gives you programmatic access to BuildVoiceAI. Use the API to trigger outbound calls from your own applications, and configure webhooks to receive real-time call event data in Zapier, Make, n8n, or any custom endpoint.
Prerequisites
- A BuildVoiceAI account on the Agency plan (or a client with API access enabled)
- A server or application capable of making HTTP requests (for the API) and receiving them (for webhooks)
Setup
Generate an API Key
- Navigate to Settings → API & Webhooks (agency-level) or Client Detail → API Keys (client-level)
- Click Generate API Key
- Copy the API key and store it securely
Your API key is shown only once when generated. If you lose it, you will need to generate a new one, which invalidates the previous key.
Enable/Disable API Access
You can toggle API access on or off without deleting your API key:
- Go to Settings → API & Webhooks
- Use the Enable/Disable toggle
When disabled, all API requests will return a 403 Forbidden response.
Set a Webhook URL
To receive call event data, configure a webhook endpoint:
- On the API & Webhooks section, find Webhook URL
- Enter the HTTPS URL of your endpoint (e.g., from Zapier, Make, n8n, or your own server)
- Click Save
A webhook signing secret is automatically generated when you save your configuration. Use this secret to verify that incoming webhooks are authentically from BuildVoiceAI.
Client-level webhook URLs override the agency default. If a client has its own webhook URL configured, call events for that client’s agents are sent to the client URL instead of the agency URL.
Test Your Webhook
After saving your webhook URL, click Send Test Webhook to verify the connection:
- Click the Send Test Webhook button
- A sample
call_endedpayload is sent to your configured URL - The test payload includes
"test": trueso your automation can distinguish test from live events - Check the result — success or failure — displayed immediately
Default Agent
Select the default agent for API-triggered calls:
- Find Default Agent on the integration page
- Select an agent from the dropdown
- Click Save
This agent is used when an API call request does not specify an agent_id.
API Authentication
Include your API key in the Authorization header of every request:
Authorization: Bearer your_api_key_hereAll API requests must be made over HTTPS.
Triggering Outbound Calls
To trigger an outbound call via the API, send a POST request:
curl -X POST https://app.buildvoiceai.com/api/trigger-call \
-H "Authorization: Bearer your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"phone": "+15551234567",
"name": "Jane Smith",
"email": "jane@example.com",
"agent_id": "optional-agent-uuid"
}'Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
phone | string | Yes | Phone number in E.164 format |
name | string | No | Caller name, passed to the agent’s prompt context |
email | string | No | Caller email |
agent_id | string | No | UUID of the agent to use. Falls back to the default agent if omitted. |
metadata | object | No | Arbitrary key-value pairs passed through to webhooks |
Response
A successful request returns:
{
"success": true,
"call_id": "uuid-of-the-initiated-call"
}Webhook Events
When a webhook URL is configured, BuildVoiceAI sends POST requests to your endpoint when call events occur.
Supported Events
| Event | When It Fires | Providers |
|---|---|---|
call_started | A call begins ringing/connecting | Retell, Vapi |
call_ended | A call finishes (completed, failed, or no-answer) | Retell, Vapi, Bland, ElevenLabs |
test | Manual test from the Send Test Webhook button | All |
Bland AI and ElevenLabs only fire webhooks on call completion — there is no call_started event for Bland or ElevenLabs.
call_ended Payload
{
"event": "call_ended",
"call_id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210",
"agent_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"agent_name": "Sales Qualifier",
"status": "completed",
"direction": "outbound",
"duration_seconds": 185,
"cost_cents": 28,
"from_number": "+15551234567",
"to_number": "+15559876543",
"transcript": "Agent: Hello, how can I help you today?\nUser: I was wondering about your pricing.",
"recording_url": "https://storage.example.com/recordings/abc123.mp3",
"summary": "Caller expressed interest in the enterprise plan and requested a follow-up demo.",
"sentiment": "positive",
"started_at": "2026-03-16T14:30:00.000Z",
"ended_at": "2026-03-16T14:33:05.000Z",
"metadata": {},
"provider": "retell"
}call_started Payload
{
"event": "call_started",
"call_id": "f9e8d7c6-b5a4-3210-fedc-ba9876543210",
"agent_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"agent_name": "Sales Qualifier",
"status": "in-progress",
"direction": "inbound",
"from_number": "+15551234567",
"to_number": "+15559876543",
"started_at": "2026-03-16T14:30:00.000Z",
"metadata": {},
"provider": "vapi"
}Payload Fields
| Field | Type | Present In | Description |
|---|---|---|---|
event | string | Both | call_started, call_ended, or test |
call_id | string | Both | Unique call identifier |
agent_id | string | Both | Prosody agent UUID |
agent_name | string | Both | Human-readable agent name |
status | string | Both | in-progress, completed, failed, no-answer |
direction | string | Both | inbound or outbound |
from_number | string | Both | Caller phone number (E.164) |
to_number | string | Both | Recipient phone number (E.164) |
started_at | string | Both | ISO 8601 timestamp |
duration_seconds | number | call_ended | Call length in seconds |
cost_cents | number | call_ended | Call cost in cents |
transcript | string | call_ended | Full conversation transcript |
recording_url | string|null | call_ended | Link to call recording |
summary | string | call_ended | AI-generated call summary |
sentiment | string | call_ended | positive, neutral, or negative |
ended_at | string | call_ended | ISO 8601 timestamp |
metadata | object | Both | Custom metadata attached to the call |
provider | string | Both | retell, vapi, bland, elevenlabs, or test |
test | boolean | Test only | true on test webhooks |
Webhook Signing
Every webhook request is signed with HMAC-SHA256 when a signing secret is configured. Two headers are included:
| Header | Description |
|---|---|
X-Prosody-Signature | HMAC-SHA256 hex digest of the payload |
X-Prosody-Timestamp | Unix timestamp (seconds) when the signature was generated |
The signature is computed as:
HMAC-SHA256(signing_secret, "${timestamp}.${body}")Verification Example (Node.js)
const crypto = require('crypto');
function verifyWebhook(rawBody, signature, timestamp, signingSecret) {
// Reject old payloads to prevent replay attacks
const age = Math.floor(Date.now() / 1000) - parseInt(timestamp);
if (age > 300) throw new Error('Webhook too old');
const expected = crypto
.createHmac('sha256', signingSecret)
.update(`${timestamp}.${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.
The signing secret is separate from your API key. You can find it in Settings → API & Webhooks — click the eye icon to reveal it.
Retry Policy
If your webhook endpoint returns a non-2xx status code or does not respond within 10 seconds, BuildVoiceAI retries the request with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st attempt | Immediate |
| 2nd attempt (1st retry) | 1 second after failure |
| 3rd attempt (2nd retry) | 2 seconds after failure |
Only server errors (5xx) and rate limits (429) are retried. Client errors (4xx except 429) fail immediately since retrying will not help.
After 3 failed attempts, the delivery is marked as failed. All delivery attempts are logged in the Delivery Log.
Delivery Log
View recent webhook delivery attempts in Settings → API & Webhooks → Recent Deliveries. Each entry shows:
- Event type (
call_started,call_ended,test) - Timestamp
- HTTP status code returned by your endpoint
- Success or failure status
- Error message (if applicable)
- Attempt number
Use the delivery log to debug failed webhooks, verify test payloads were received, and monitor overall delivery health.
Troubleshooting
API Returns 403
- Verify API access is enabled on the integration page
- Check that the API key in your
Authorizationheader is correct - Ensure you are using
Bearertoken format
Webhook Not Receiving Events
- Confirm your webhook URL is publicly accessible (not behind a firewall or VPN)
- Verify the URL returns a
200status code within 10 seconds - Use the Send Test Webhook button to verify connectivity
- Check the Delivery Log for error details
- Ensure HTTPS is used (HTTP endpoints are rejected)
Signature Verification Failing
- Use the signing secret (not the API key) for HMAC verification
- Use the raw request body (not parsed JSON) when computing the signature
- Include the timestamp: the signature covers
"${timestamp}.${body}" - Reject payloads older than 5 minutes to prevent replay attacks
Calls Not Being Placed
- Verify the
phonefield is in E.164 format (e.g.,+15551234567) - Check that the default agent is set (or a valid
agent_idis provided) - Ensure the agent has a phone number assigned
- Confirm the calling window allows calls at the current time