The /attach route joins an already-answered LiveKit room and runs the bot pipeline. It is the entry point CXB Dialler uses once it has dialed, screened (ring/answer/AMD), and confirmed a live callee. CXB Core joins as the bot participant; the SIP leg (the callee) is already in the room. This differs from LiveKit SIP outbound: with /attach, CXB Core does not dial — CXB Dialler owns dialing and pacing, CXB Core only runs the conversation.

Connection flow

Endpoint

POST /attach
Requires the X-CXB-Core-Secret header (403 without).

Request body

{
  "room_name": "campaign-room-abc",
  "bot_id": "bot-uuid",
  "to_number": "+919876543210",
  "from_number": "+911234567890",
  "config_url": "https://api.cxbridge.io/api/v1/config",
  "connected_event": {"campaign_id": "abc"},
  "campaign_call_id": "lease-uuid"
}
FieldTypeRequiredDescription
room_namestringYesExisting LiveKit room the callee is already in
bot_idstringYesBot configuration to use
to_numberstringNoCallee number (also the SIP participant identity suffix sip-{to_number})
from_numberstringNoCaller ID / DID
config_urlstringNoMulti-client passthrough — overrides CXB_API_CONFIG_URL for the config fetch
connected_eventobjectNoCRM/campaign metadata forwarded to CXB API and used for Agent Desk handoff variables
campaign_call_idstringNoCXB Dialler lease identifier

Response

{
  "status": "accepted",
  "call_id": "uuid-string",
  "room_name": "campaign-room-abc"
}
The endpoint returns immediately; the pipeline runs in a background task.

Speaks immediately

Because the callee already answered before CXB Dialler attaches, the route sets the call_answered event immediately (call_answered.set() before the pipeline starts). There is no ring-back gating — the bot can queue the opening message as soon as it joins.

Error-result fallback

If config fetch fails (config is None), there is no bot to run post-call analysis. Instead of dropping the call silently, the route POSTs a minimal error result directly to CXB API’s /call-results (base URL derived from config_url or CXB_API_CONFIG_URL), matching the CallResultsPayload schema with zero duration and the determined disconnected_by. This ensures CXB Dialler/CXB API always learn the outcome of an attached call.

Capacity

The route returns 429 At capacity when the worker has no free slot. /attach is in the fleet nginx 429-retry list (proxy_next_upstream ... http_429), so nginx retries another worker — safe because CXB Core returns 429 before starting any side effects.

Agent Desk handoff

The attached call supports human-agent escalation via the transfer_call tool / agent_desk_callback, identical to the other LiveKit routes: enqueue a durable handoff to CXB API, start a hold worker, and set disconnected_by = "transfer_to_agent" on success.