Prerequisites: API key and an agent configured with webhook settings.
Overview
Voice.ai supports three webhook types.webhooks.events[], webhooks.inbound_call, and webhooks.tools use different contracts:
webhooks.events[]supportssecret(write-only on create/update) andhas_secret(read-only on fetch), with fan-out across enabled endpoints.webhooks.inbound_callsupportssecret(write-only on create/update) andhas_secret(read-only on fetch).webhooks.toolsdefine outbound API calls and do not usesecret.
| Type | Direction | Purpose | Auth |
|---|---|---|---|
| Event Webhooks | Inbound: Voice.ai → your URL | Notify your system about call lifecycle events | Use secret for HMAC signature verification |
| Inbound Call Webhook | Inbound: Voice.ai → your URL | Personalize inbound calls with dynamic_variables | Use secret for HMAC signature verification |
| Webhook Tools | Outbound: Voice.ai calls your API | Let the agent invoke your application during a call | Use auth_type/auth_token/headers for request authentication |
| Event | Description |
|---|---|
call.started | Call has connected and the agent is ready |
call.completed | Call has ended, includes transcript and usage data |
Example routes
The runnable receiver example and the verification snippets below use these canonical routes:| Config field | Example URL | Method | Purpose |
|---|---|---|---|
webhooks.events[].url | https://your-server.com/webhooks/voice-events | POST | Receive signed call.started and call.completed events |
webhooks.inbound_call.url | https://your-server.com/webhooks/inbound-call | POST | Return dynamic_variables and optional agent_overrides before an inbound phone call starts |
webhooks.tools[0].url | https://your-server.com/webhooks/tools/account-status | POST | Return account status data for a sync tool |
webhooks.tools[1].url | https://your-server.com/webhooks/tools/search-kb | GET | Accept search parameters for a tool request |
Event Webhooks
Configuration
Add event webhook configuration to your agent’sconfig.webhooks.events array:
Event configuration parameters
Each entry inwebhooks.events[] supports required/optional fields: required url; optional secret, events, timeout (default 5), enabled (default true). Omit optional fields to use defaults.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
url | string | Yes | - | Your webhook endpoint URL (must be HTTPS in production) |
secret | string | No | null | HMAC-SHA256 signing secret for payload verification |
has_secret | boolean | No | false | Whether a signing secret is configured (read-only on fetch) |
events | array | No | [] | Event types to receive. Empty array = all events |
timeout | number | No | 5 | Request timeout in seconds (1-30) |
enabled | boolean | No | true | Whether webhook notifications are active |
webhooks.events to preserve the current list, set webhooks.events: null to clear it, or pass a full array to replace it. Within a replacement array, omitted secret values are preserved only for entries whose url exactly matches an existing endpoint, secret: null clears that endpoint’s signing secret, duplicate URLs are invalid, and events: [] means that endpoint receives all event types.
Event Payloads
All webhook events share a common structure with top-level fields and event-specific data:| Field | Type | Description |
|---|---|---|
event | string | Event type (call.started, call.completed) |
timestamp | string | ISO 8601 timestamp of when the event occurred |
call_id | string | Unique identifier for the call |
agent_id | string | Agent handling the call |
data | object | Event-specific additional data |
call.started
Sent when a call connects and the agent is ready to interact.data fields:
| Field | Type | Description |
|---|---|---|
call_type | string | "web", "sip_inbound", or "sip_outbound" |
started_at | string | ISO 8601 timestamp |
from_number | string | Caller’s phone number (SIP calls only) |
to_number | string | Called phone number (SIP calls only) |
from_number and to_number are only included for SIP (phone) calls. Web calls do not have these fields.call.completed
Sent after the call ends and all data has been processed. Includes transcript and usage information.data fields:
| Field | Type | Description |
|---|---|---|
call_type | string | "web", "sip_inbound", or "sip_outbound" |
duration_seconds | number | Call duration in seconds |
credits_used | number | Credits consumed by this call |
transcript_uri | string | URL to full transcript JSON |
transcript_summary | string | AI-generated summary of the call |
from_number | string | Caller’s phone number (SIP calls only) |
to_number | string | Called phone number (SIP calls only) |
Inbound Call Webhook
webhooks.inbound_call runs before an inbound phone call starts. Use it to personalize a call with dynamic_variables and optional agent_overrides. dynamic_variables can be referenced by both the runtime prompt and greeting. Do not use it to route a call to a different agent.
Configuration
Add inbound call webhook configuration to your agent’sconfig.webhooks.inbound_call object:
Inbound call configuration parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
url | string | Yes | - | Your inbound call webhook endpoint URL |
secret | string | No | null | HMAC-SHA256 signing secret for payload verification |
has_secret | boolean | No | false | Whether a signing secret is configured (read-only on fetch) |
timeout | number | No | 5 | Request timeout in seconds (1-30) |
enabled | boolean | No | true | Whether inbound call personalization is active |
webhooks.inbound_call to preserve it, set webhooks.inbound_call: null to remove it, and set secret: null to clear only the signing secret.
Request payload
Voice.ai sends a POST request with the inbound call context:| Field | Type | Description |
|---|---|---|
agent_id | string | Agent receiving the inbound call |
call_id | string | Unique identifier for the call |
from_number | string | Caller’s phone number |
to_number | string | Number the caller dialed |
Response payload
Your endpoint can returndynamic_variables and optional agent_overrides:
| Field | Type | Description |
|---|---|---|
dynamic_variables | object | Optional flat object of string, number, or boolean values |
agent_overrides | object | Optional runtime config overrides |
dynamic_variables are ignored by the runtime. Use agent_overrides for allowlisted call-scoped config changes such as tts_params overrides. dynamic_variables can be used in both your saved agent prompt and greeting. See the Web SDK guide for browser-side examples.
Webhook Tools
Tools are outbound API calls: Voice.ai calls your endpoint. Useauth_type/auth_token/headers (not secret - that is only for signed inbound webhooks like events and inbound_call).
Use config.webhooks.tools to declare callable functions:
Tool configuration parameters
tools[] supports required/optional fields per tool: required name, description, parameters, url, method, execution_mode, auth_type; optional auth_token, headers, response, timeout (default 10). Omit optional fields to use defaults.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | - | Tool name used in function_name |
description | string | Yes | - | Human-readable tool behavior description |
url | string | Yes | - | Your API endpoint URL |
parameters | object | Yes | - | Tool argument schema |
method | string | Yes | - | GET, POST, PUT, PATCH, or DELETE |
execution_mode | string | Yes | - | sync (wait for response) or async (accept 2xx) |
auth_type | string | Yes | - | none, bearer_token, api_key, or custom_headers |
auth_token | string | No | null | Token for bearer_token or api_key auth |
headers | object | No | {} | Custom headers (for auth_type: custom_headers) |
response | object | No | {} | Expected response shape |
timeout | number | No | 10 | Request timeout in seconds |
webhooks.tools to leave the current tool list unchanged, set webhooks.tools: null to clear all tools, or pass a new array to replace the current list.
Tool request shape
Voice.ai makes HTTP requests directly to your tool URL:- GET: Arguments as query parameters
- POST/PUT/PATCH/DELETE: Arguments as JSON body
X-VoiceAI-Request-Id, X-VoiceAI-Tool-Name, X-VoiceAI-Agent-Id, X-VoiceAI-Call-Id
Example GET request:
Tool authentication
auth_type: 'none': no auth headers added.auth_type: 'bearer_token': sendsAuthorization: Bearer <auth_token>.auth_type: 'api_key': sendsX-API-Key: <auth_token>.auth_type: 'custom_headers': sends your configuredheadersmap.
Tool response behavior
execution_mode: 'sync': waits for downstream response body; non-2xx fails the tool call.execution_mode: 'async': treats any 2xx as accepted and does not require a response payload.
Signed Inbound Webhook Headers
webhooks.events[] and webhooks.inbound_call requests include:
| Header | Description |
|---|---|
Content-Type | application/json |
X-Webhook-Timestamp | Unix timestamp of when the request was signed |
X-Webhook-Signature | HMAC-SHA256 signature (only if webhooks.events[].secret or inbound_call.secret is configured) |
Signature Verification (Event and Inbound Call Webhooks)
webhooks.events[] and webhooks.inbound_call use secret for HMAC-SHA256. If you configure either secret, verify signatures to ensure requests are from Voice.ai. Tool webhooks use auth_type/auth_token/headers instead and do not use HMAC.
Signature Format
The signature is computed as:timestampis the value fromX-Webhook-Timestampheaderpayloadis the raw JSON request body
Verification Examples
Use the same verifier for both event webhooks and inbound call webhooks. The examples below expose the same four routes listed above: one event endpoint, one inbound call endpoint, and two tool endpoints.Retry Logic
Failed webhook deliveries are retried with exponential backoff:| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 second |
| 3 | 2 seconds |
| 4 | 4 seconds |
| 5 | 8 seconds |
Best Practices
- Always verify signatures in production to prevent spoofed requests
- Respond quickly with a 2xx status code within 5 seconds to avoid retries
- Process asynchronously - queue events for processing rather than blocking the response
- Handle duplicates - use
call_idto deduplicate in case of retries - Check timestamps - reject signed requests older than 5 minutes to prevent replay attacks
- Use HTTPS - ensure your webhook endpoint uses TLS encryption
Filtering Events
You can filter which events you receive by specifying theevents array:
call.completed events. Leave events empty or omit it to receive all event types.
Testing Webhooks
You can test your configured event webhook endpoints using the Test Events Webhook endpoint:test event to each enabled configured webhooks.events[] endpoint and returns per-endpoint delivery results. It does not invoke webhooks.inbound_call.
Testing Tools Webhooks
You can test an individual tools webhook using the Test Tools Webhook endpoint:function_call payload for the specified webhooks.tools[] entry and returns the delivery result. It does not invoke webhooks.inbound_call.
Testing Inbound Call Webhooks
You can test your configured inbound call webhook using the Test Inbound Call Webhook endpoint:webhooks.inbound_call and validates the response before you use it in a live call. It does not place a real phone call.
The test endpoint is intentionally strict:
dynamic_variablesmust be valid scalar values and may only include variables referenced by the agent’s savedpromptorgreetingagent_overridesmust match the runtime override schema- returned TTS overrides are validated and normalized the same way runtime inbound-call overrides are, including
language -> model,voice_id -> speaker, dictionary resolution, andbase_url
Next Steps
- Agent Configuration - Configure prompts,
dynamic_variables, and webhook settings - Web SDK - Pass
dynamicVariablesfrom the browser and configure webhooks through the SDK - Analytics - View call history and metrics
- API Reference - Complete API documentation