Skip to content

Inbound Call Flow ​

Overview ​

Inbound calls are routed through LiveKit's SIP infrastructure and resolved dynamically at call time. There is no static agent identity in dispatch metadata resolution happens on every call.

LiveKit Routing ​

Caller → SIP Provider → LiveKit Inbound Trunk → Dispatch Rule → Room Created → Agent Joins
  1. Trunk matching: LiveKit matches the incoming call by the dialed phone number against registered inbound trunks
  2. Dispatch rule: The matched trunk's dispatch rule creates a new LiveKit room
  3. Agent dispatch: LiveKit dispatches the configured agent (AGENT_NAME) to join the room

Agent Resolution (Python Agent) ​

When the Python agent joins the room, it resolves the correct configuration:

  1. Read trunk phone number: Agent reads sip.trunkPhoneNumber from the SIP participant attributes
  2. Normalize number: Adds + prefix if not present (E.164 normalization)
  3. Call backend resolution API: Sends the normalized number to the backend

Resolution API ​

POST /internal/resolve-inbound-number

Request:

json
{
  "phoneNumber": "+15551234567"
}

Response:

json
{
  "agentId": "...",
  "prompt": "...",
  "llmConfig": { ... },
  "ttsConfig": { ... },
  "sttConfig": { ... },
  "firstMessage": "...",
  // Full agent configuration
}

Agent Config Loading ​

The agent session is built AFTER resolution completes:

  1. Agent joins room with minimal/default config
  2. Agent reads SIP attributes to get the phone number
  3. Agent calls resolution API with the phone number
  4. Backend looks up Agent.inboundPhoneNumber to find the linked agent
  5. Backend returns the full agent configuration (LLM, TTS, STT, prompt, tools, etc.)
  6. Agent builds the session with the resolved configuration

This ensures each inbound number can serve a completely different agent personality and configuration.

Contact Creation ​

Contacts are created in the webhook handler, not during the call itself:

  • Caller information is read from SIP attributes on the participant (not from the room name)
  • SIP attributes include caller number, display name, and other SIP headers
  • The webhook handler processes these after the call connects

Transcription Checkpoint ​

The transcription system reads meta.identity dynamically during the call. This allows it to correctly attribute utterances even when the agent identity isn't known until after resolution.

Runtime vs Static Architecture ​

AspectApproach
Dispatch metadataMinimal: { mode: "inbound", need_to_update_agent: true }
Agent identityNOT in dispatch metadata
Agent resolutionRuntime, via /internal/resolve-inbound-number
Config loadingAfter resolution, per-call
Number → Agent mappingAgent.inboundPhoneNumber field in MongoDB

This runtime resolution architecture allows:

  • Changing agent assignments without updating LiveKit dispatch rules
  • Multiple numbers sharing the same trunk infrastructure but different agents
  • Hot-swapping agent configs without any LiveKit-side changes

Last updated: