How CXB Core works
CXB Core is a stateless pipeline worker. It doesn’t store bots, users, or call history. For each call:- It fetches bot configuration from a URL you provide (
CONFIG_URL) - It runs the voice pipeline (STT → LLM → TTS)
- It persists and POSTs call results to the
webhook_urlfrom the config
- Config endpoint — CXB Core calls this to get bot config at call start
- Results webhook — CXB Core calls this to deliver call results at call end
1. Config endpoint (you implement)
CXB Core fetches bot configuration at the start of every call.Request (CXB Core → your system)
| Parameter | Source | Description |
|---|---|---|
bot_id | URL path from WebSocket route or dialout request | Which bot to load |
caller_id | Dialler handshake or SIP headers | Customer phone number |
stream_id | Dialler handshake | Unique call identifier from the dialler |
connected_event | URL-encoded JSON | Full handshake payload from dialler, attach, or dialout request (CRM fields, call direction, etc.) |
X-CXBCore-Secret | .env on fleet server | Shared secret for authentication |
Response (your system → CXB Core)
Return200 with the full bot config JSON. See Bot config schema for the complete field reference.
Minimal example:
| Status | Meaning | CXB Core behavior |
|---|---|---|
200 | Config returned | Call proceeds |
404 | Bot not found | Call rejected, WebSocket closed |
503 | Outside active hours | Call rejected, WebSocket closed |
| Other | Error | Call rejected, WebSocket closed |
The
session_id you return must be unique per call. CXB Core uses it to correlate the results webhook back to this call. Generate a UUID.2. Results webhook (you implement)
CXB Core POSTs call results when a call ends. The URL comes fromwebhook_url in the config response.
Request (CXB Core → your system)
Payload
Field reference
- Core fields
- Transcript
- Analysis
- Transfer
- Metrics
| Field | Type | Description |
|---|---|---|
session_id | string | Session ID from config response |
stream_id | string | Dialler-assigned call identifier |
caller_id | string | Customer phone number |
from_number | string/null | Outbound caller ID / DID used for the call |
call_duration_seconds | float | Total call length in seconds |
call_direction | string | "inbound" or "outbound" |
disconnected_by | string | Who ended the call (see table below) |
transcript | list | Conversation transcript (see Transcript tab) |
recording_url | string | URL to call recording (WAV) |
recording_key | string | S3 key for generating signed URLs |
usage_metrics entries
Each entry records usage for one pipeline step.
| Field | Type | Description |
|---|---|---|
type | string | llm, tts, ttfb, post_call, or qc. Callback extraction is emitted as type=post_call with processor=callback_extraction. |
processor | string/null | Pipeline service class (or post-call stage) that produced the metric |
model | string/null | Model identifier |
prompt_tokens | int/null | LLM input tokens |
completion_tokens | int/null | LLM output tokens |
total_tokens | int/null | LLM total tokens |
cache_read_input_tokens | int/null | Tokens served from cache (cache read) |
cache_creation_input_tokens | int/null | Tokens written when creating/refreshing a cache |
cache | dict/null | Cache metadata (see below) |
characters | int/null | TTS character count (TTS only) |
ttfb_ms | float/null | Time-to-first-byte in ms (TTFB only) |
cache dict:
| Key | Type | Description |
|---|---|---|
enabled | bool | Whether caching was active for this step |
status | string | hit / miss for live LLM; cache create/reuse status for post-call |
namespace | string | live_prompt (first live llm entry), post_call_analysis, qc_analysis, or callback_extraction |
version | string | Cache version in effect |
reason | string/null | Why the cache was in this state (e.g. recreated/expired) |
For LLM providers that report cache reads, live-prompt
hit/miss is derived only from real cache_read_input_tokens (hit when > 0, miss when 0). CXB Core does not estimate cached tokens or money saved. Cache-creation tokens come from the provider’s cache usage metadata. See Caching.events entries
events is an open-ended list of timestamped lifecycle entries. Every entry has event (string) and ts (float, seconds from call start). Depending on the event type, additional keys may be present:
| Field | Description |
|---|---|
by | Actor for disconnect events (customer, bot, …) |
trigger | Hangup trigger: end_call_tool, closure_phrase, dead_air_timeout, max_duration |
function | Tool name for tool-call events (end_call, transfer_call, detected_voicemail, custom) |
args | Arguments passed to the tool |
status | Tool status, e.g. ok / no_number_configured |
transfer_number / transfer_headers | Transfer destination and SIP headers |
reason | Human-readable reason (e.g. SIP failure description) |
sip_code / sip_status | SIP response code and status text |
error | Error string or dict (service errors) |
duration_ms / duration_seconds / raw_duration_seconds / billable_duration_seconds | Duration fields |
note | Free-form note |
disconnected_by values
| Value | Meaning |
|---|---|
bot | Bot called end_call tool, closure phrase, or dead air timeout |
customer | Customer hung up |
voicemail | Voicemail detected |
RNR | Dead air — customer picked up but stayed silent |
outside_hours | Call rejected — bot outside active hours |
no_answer | SIP 408/480 — callee didn’t answer (outbound only) |
rejected | SIP 486/603 — callee rejected (outbound only) |
timeout | Max call duration exceeded |
transfer_to_agent | Call handed off to Agent Desk; route timeout must not tear it down |
error | Pipeline startup failure or unhandled exception |
Your response
Return200 to acknowledge. CXB Core first writes results to a local durable outbox, then attempts delivery. Transient transport failures and 5xx responses are retried immediately a few times; any failed delivery attempt remains in the outbox and the background worker retries later.
3. Dialout endpoint (on CXB Core)
Trigger outbound calls by POSTing to CXB Core’s dialout endpoint. This requires LiveKit SIP to be configured on the fleet server.Request (your system → CXB Core)
| Field | Type | Required | Description |
|---|---|---|---|
bot_id | string | Yes | Bot ID passed to config endpoint |
to_number | string | Yes | Customer phone number to dial |
from_number | string | No | Caller ID / DID. Uses default trunk number if omitted. |
connected_event | dict | No | CRM data available as {{call.field}} in prompts |
config_url | string | No | Override config endpoint URL. Falls back to the CONFIG_URL env var. |
The CXB Core dialout body uses
connected_event. variables is a CXB API-only alias — CXB API maps variables to connected_event before proxying to CXB Core. When calling CXB Core directly, send connected_event.config_url lets a shared CXB Core fleet serve multiple orchestrators. Each orchestrator passes its own config URL so CXB Core fetches the right bot config.Response
| Status | Body | Meaning |
|---|---|---|
200 | {"status":"accepted","call_id":...,"room_name":...} | Call accepted (runs in background) |
403 | {"error":"Unauthorized"} | Invalid X-CXBCore-Secret |
429 | {"error":"At capacity"} | Worker has no free call slot |
503 | {"error":"LiveKit not configured"} | LiveKit SIP not configured on this fleet host |
For campaign dialing, CXB Dialler usually creates and screens the LiveKit SIP call first, then calls CXB Core
POST /attach after the callee answers. Use /livekit/dialout for direct one-off CXB Core outbound calls.