This guide covers how to integrate with the platform programmatically — trigger outbound calls, receive call results, and pass CRM data to the bot. Use this if you’re building your own UI or connecting from a CRM/dialler backend.

Overview

Authentication

All API calls require an API key in the X-API-Key header.
X-API-Key: your-api-key
API keys are created in the dashboard under Settings → API Keys, or by your admin. Each key is scoped to specific bots.

Trigger an outbound call

Outbound calls require a SIP trunk configured in the platform. Your admin sets this up in the dashboard.
POST /api/v1/sip/dialout

Request

{
  "bot_id": "your-bot-id",
  "to_number": "+919876543210",
  "from_number": "+911234567890",
  "variables": {
    "customer_name": "Rahul Sharma",
    "loan_number": "LN123456",
    "amount_due": "15000",
    "due_date": "2026-05-01"
  }
}
FieldTypeRequiredDescription
bot_idstringYesBot ID (from the dashboard bot settings)
to_numberstringYesCustomer phone number (E.164 format recommended)
from_numberstringNoCaller ID / DID to use. If omitted, uses the default outbound trunk number.
variablesobjectNoCRM data passed to the bot. Available in prompts as {{crm.field_name}}.

Variables

The variables 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:
Hello, am I speaking with {{crm.customer_name}}?
And you send "variables": {"customer_name": "Rahul Sharma"}, the bot will say:
Hello, am I speaking with Rahul Sharma?
All values are stringified. Nested objects are JSON-encoded.

Response

{
  "status": "ok",
  "call_id": "abc123-def456"
}

Errors

StatusMeaning
200Call initiated successfully
401Missing or invalid API key
404Bot not found
422Invalid request body
503No healthy fleet server available, or bot outside active hours

curl example

curl -X POST https://api.example.com/api/v1/sip/dialout \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{
    "bot_id": "your-bot-id",
    "to_number": "+919876543210",
    "variables": {
      "customer_name": "Rahul Sharma",
      "amount_due": "15000"
    }
  }'

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):
{
  "url": "https://your-crm.com/api/call-result",
  "token": "Bearer your-token",
  "method": "POST",
  "key_mapper": {
    "CUSTOMER_ID": "{{crm.customer_id}}",
    "DISPOSITION": "{{result.disposition}}",
    "REMARKS": "{{result.remarks}}",
    "CALL_DURATION": "{{call.duration}}",
    "RECORDING_URL": "{{call.recording_url}}"
  }
}
The platform resolves all {{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:
wss://fleet.example.com/ws/{bot_id}
See the WebSocket protocol reference for the full handshake and message format. The connected event in the handshake can carry CRM data in any custom fields — they become available as {{call.field_name}} in prompts:
{
  "event": "connected",
  "callerId": "+919876543210",
  "streamId": "stream-123",
  "customer_name": "Rahul Sharma",
  "loan_number": "LN123456"
}

Call result schema

Whether received via webhook or CRM push, call results include:
FieldTypeDescription
session_idstringUnique call identifier
caller_idstringCustomer phone number
from_numberstringCaller ID / DID used on the call leg
call_duration_secondsfloatTotal call length
disconnected_bystringWho/what ended the call. See enum below.
transcriptarrayConversation transcript (speaker + text + timestamp)
recording_urlstringURL to call recording (WAV)
recording_keystringS3/object-storage key for the recording, used by CXB API to mint signed/stable playback URLs
post_call_analysisobjectLLM-generated analysis (disposition, summary, etc.) — schema defined by bot’s analysis prompt

disconnected_by values

ValueMeaning
botBot ended the call (end_call, closure phrase, or dead air)
customerCustomer hung up
voicemailVoicemail detected
RNRCustomer picked up but stayed silent throughout
outside_hoursRejected — bot unavailable or outside its active-hours window
no_answerOutbound SIP 408/480 — callee did not answer
rejectedOutbound SIP 486/603 — callee rejected the call
timeoutMax call duration exceeded
transfer_to_agentCall handed off to a human agent via Agent Desk
errorPipeline 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.
FieldTypeDescription
was_transferredboolWhether the transfer succeeded
transfer_destinationstringResolved destination address
transfer_targetstringConfigured transfer target name
transfer_reasonstringReason the LLM gave for transferring
transfer_methodstringwebsocket_reverse_transfer (WebSocket) or livekit_sip_refer (LiveKit)
transfer_atfloatTimestamp of the transfer within the call
transfer_failed_reasonstringPopulated 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/fleet before 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.