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- Trunk matching: LiveKit matches the incoming call by the dialed phone number against registered inbound trunks
- Dispatch rule: The matched trunk's dispatch rule creates a new LiveKit room
- 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:
- Read trunk phone number: Agent reads
sip.trunkPhoneNumberfrom the SIP participant attributes - Normalize number: Adds
+prefix if not present (E.164 normalization) - Call backend resolution API: Sends the normalized number to the backend
Resolution API
POST /internal/resolve-inbound-numberRequest:
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:
- Agent joins room with minimal/default config
- Agent reads SIP attributes to get the phone number
- Agent calls resolution API with the phone number
- Backend looks up
Agent.inboundPhoneNumberto find the linked agent - Backend returns the full agent configuration (LLM, TTS, STT, prompt, tools, etc.)
- 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
| Aspect | Approach |
|---|---|
| Dispatch metadata | Minimal: { mode: "inbound", need_to_update_agent: true } |
| Agent identity | NOT in dispatch metadata |
| Agent resolution | Runtime, via /internal/resolve-inbound-number |
| Config loading | After resolution, per-call |
| Number → Agent mapping | Agent.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