How VoxCore works
VoxCore 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 (
VOXBRIDGE_CONFIG_URL) - It runs the voice pipeline (STT → LLM → TTS)
- It persists and POSTs call results to the
webhook_urlfrom the config
- Config endpoint — VoxCore calls this to get bot config at call start
- Results webhook — VoxCore calls this to deliver call results at call end
1. Config endpoint (you implement)
VoxCore fetches bot configuration at the start of every call.Request (VoxCore → 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-VoxCore-Secret | .env on fleet server | Shared secret for authentication |
Response (your system → VoxCore)
Return200 with the full bot config JSON. See Bot config schema for the complete field reference.
Minimal example:
| Status | Meaning | VoxCore 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. VoxCore uses it to correlate the results webhook back to this call. Generate a UUID.2. Results webhook (you implement)
VoxCore POSTs call results when a call ends. The URL comes fromwebhook_url in the config response.
Request (VoxCore → 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 | Pipecat 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 OpenAI, live-prompt
hit/miss is derived only from real cache_read_input_tokens (hit when > 0, miss when 0). VoxCore does not estimate cached tokens or money saved. Gemini/Vertex 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. VoxCore 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 VoxCore)
Trigger outbound calls by POSTing to VoxCore’s dialout endpoint. This requires LiveKit SIP to be configured on the fleet server.Request (your system → VoxCore)
| 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 VOXBRIDGE_CONFIG_URL env var. |
The VoxCore dialout body uses
connected_event. variables is a VoxBridge-only alias — VoxBridge maps variables to connected_event before proxying to VoxCore. When calling VoxCore directly, send connected_event.config_url lets a shared VoxCore fleet serve multiple orchestrators. Each orchestrator passes its own config URL so VoxCore 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-VoxCore-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, VoxDialler usually creates and screens the LiveKit SIP call first, then calls VoxCore
POST /attach after the callee answers. Use /livekit/dialout for direct one-off VoxCore outbound calls.