call-* room (per the dispatch rule created in carriers). LiveKit then fires a participant_joined webhook to CXB API, which picks a fleet worker and hands the call to CXB Core.
The webhook handler lives in the API service’s LiveKit-webhook route.
Endpoint
POST /api/v1/livekit/webhook — no JWT/secret dependency; authenticated by the LiveKit webhook signature instead.
| Step | Behaviour |
|---|---|
| Auth | Authorization header (optionally Bearer -prefixed) verified by WebhookReceiver + TokenVerifier using livekit_api_key/secret. Missing → 401 Missing auth; bad signature → 401 Invalid signature. |
| Event filter | Ignores any event other than participant_joined (returns {ok: true}). |
| Participant filter | Ignores non-SIP participants (participant.kind != SIP). |
| Room filter | Ignores rooms whose name does not start with call-. |
Dispatch flow
Attribute extraction helpers:| Field | Source |
|---|---|
caller_number | sip.phoneNumber attribute, else parsed from sip_/sip- identity |
dialed_number | sip.calledNumber, else sip.trunkPhoneNumber |
trunk_id | sip.trunkID |
call_id | sip.callID |
dialed_number is empty but trunk_id is known, CXB API looks up the carrier by lk_inbound_trunk_id and uses the first DID’s number as a fallback.
Fleet handoff
core_fleetis read fromsystem_settings; empty →503 No fleet.pick_fleet_serverselects the healthiest host; none healthy →503 No healthy fleet.- Dispatch payload:
{room_name, caller_number, dialed_number, call_id}toPOST {fleet}/livekit/dispatchwithX-CXB-Core-Secretand a 10s timeout. - A
200relays CXB Core’s JSON; non-200 returns the fleet status + body; transport errors return502.
Related docs
Carriers & trunks
Inbound trunk + dispatch rule that produce
call-* rooms.Fleet routing
Health checks and best-server selection.
LiveKit SIP inbound (CXB Core)
What CXB Core’s
/livekit/dispatch does with the call.SIP dialout
The outbound counterpart.