Overview
Authentication
All API calls require an API key in theX-API-Key header.
Trigger an outbound call
Outbound calls require a SIP trunk configured in the platform. Your admin sets this up in the dashboard.
Request
| Field | Type | Required | Description |
|---|---|---|---|
bot_id | string | Yes | Bot ID (from the dashboard bot settings) |
to_number | string | Yes | Customer phone number (E.164 format recommended) |
from_number | string | No | Caller ID / DID to use. If omitted, uses the default outbound trunk number. |
variables | object | No | CRM data passed to the bot. Available in prompts as {{crm.field_name}}. |
Variables
Thevariables object lets you pass any CRM data into the call. These values are substituted into the bot’s system prompt and opening message wherever {{crm.field_name}} appears.
Example: If the bot’s opening message is:
"variables": {"customer_name": "Rahul Sharma"}, the bot will say:
Response
Errors
| Status | Meaning |
|---|---|
| 200 | Call initiated successfully |
| 401 | Missing or invalid API key |
| 404 | Bot not found |
| 422 | Invalid request body |
| 503 | No healthy fleet server available, or bot outside active hours |
curl example
Receive call results
After each call completes, the platform pushes results to a configurable endpoint. There are two mechanisms:1. CRM post-call push (recommended)
Configure a post-call push URL in the bot settings. After each call, the platform POSTs structured data to your endpoint using a key mapper — you define which fields to send and how they map to your CRM’s field names. Example key mapper configuration (set in bot settings):{{namespace.field}} templates and POSTs the result to your URL.
2. CRM pre-fetch
You can configure the bot to fetch CRM data at call start instead of passing it in the dialout request. Configure a CRM pre-fetch URL in bot settings — the platform calls your API when the call starts and uses the response data for{{crm.*}} substitutions.
This is useful when:
- Your dialler doesn’t send CRM data in the call request
- You want a single source of truth for customer data
- You’re using inbound calls (no dialout request to attach variables to)
WebSocket integration (inbound)
For diallers that connect via WebSocket (inbound calls), connect to:connected event in the handshake can carry CRM data in any custom fields — they become available as {{call.field_name}} in prompts:
Call result schema
Whether received via webhook or CRM push, call results include:| Field | Type | Description |
|---|---|---|
session_id | string | Unique call identifier |
caller_id | string | Customer phone number |
from_number | string | Caller ID / DID used on the call leg |
call_duration_seconds | float | Total call length |
disconnected_by | string | Who/what ended the call. See enum below. |
transcript | array | Conversation transcript (speaker + text + timestamp) |
recording_url | string | URL to call recording (WAV) |
recording_key | string | S3/object-storage key for the recording, used by CXB API to mint signed/stable playback URLs |
post_call_analysis | object | LLM-generated analysis (disposition, summary, etc.) — schema defined by bot’s analysis prompt |
disconnected_by values
| Value | Meaning |
|---|---|
bot | Bot ended the call (end_call, closure phrase, or dead air) |
customer | Customer hung up |
voicemail | Voicemail detected |
RNR | Customer picked up but stayed silent throughout |
outside_hours | Rejected — bot unavailable or outside its active-hours window |
no_answer | Outbound SIP 408/480 — callee did not answer |
rejected | Outbound SIP 486/603 — callee rejected the call |
timeout | Max call duration exceeded |
transfer_to_agent | Call handed off to a human agent via Agent Desk |
error | Pipeline startup failure or unhandled exception |
Transfer state
When the bot transfers a call, results carry the derived transfer fields.was_transferred is true only when a transfer_sent event fired and no later transfer_failed event invalidated it.
| Field | Type | Description |
|---|---|---|
was_transferred | bool | Whether the transfer succeeded |
transfer_destination | string | Resolved destination address |
transfer_target | string | Configured transfer target name |
transfer_reason | string | Reason the LLM gave for transferring |
transfer_method | string | websocket_reverse_transfer (WebSocket) or livekit_sip_refer (LiveKit) |
transfer_at | float | Timestamp of the transfer within the call |
transfer_failed_reason | string | Populated when a transfer was attempted but failed |
Rate limits
- Direct API dialout: no hard rate limit, but calls are bounded by fleet capacity. Check
/health/fleetbefore bulk dialing. - Direct API dialout does not queue inside CXB Core. If no workers are available, the call is rejected immediately. Campaigns should use CXB Dialler so pacing, retries, and stale recovery are handled by the campaign execution plane.
Best practices
Check capacity first
Call
GET /health/fleet before sending a batch of dialout requests. Only send as many as fleet_available allows.Pass variables, not prompts
Don’t modify the system prompt per call. Pass customer-specific data in
variables and let the prompt template handle substitution.Use unique IDs
Include a unique reference ID in
variables (e.g., customer_id) so you can correlate call results back to your CRM records.Handle 503 gracefully
A 503 means no capacity or outside active hours. Retry after a delay or queue the call on your side.