Skip to main content

Knowledge > Processes > Inbound Call Flow

Inbound Call Flow

What happens from the moment a phone rings to the moment the call ends and all data is captured. The system is multi-tenant: ONE deployed LiveKit Agents worker serves ALL churches. Per-church behavior is loaded dynamically at call time.


1. Phone Rings — Twilio Receives the Call

  1. A caller dials a Twilio phone number (toll-free, demo line, or church-specific number).
  2. Twilio forwards the call via a SIP trunk to the LiveKit Cloud SIP gateway (project: cwa-voice-9x077mph).
  3. LiveKit Cloud dispatches a job to the Railway agent worker. The worker's entry point in main.py receives a JobContext containing:
    • room.sip.trunkPhoneNumber — the number that was dialed
    • room.sip.from_ — the caller's phone number
    • room.name — unique LiveKit room identifier for this call

2. Route Resolution — Which Agent Handles This Call?

  1. resolve_route(to_number) in main.py reads sip.trunkPhoneNumber and maps the dialed number to an agent type:

    • Toll-free number (+18886030316) --> ("sales", None) --> Sales Agent
    • Demo lines (3 known numbers) --> ("demo_router", None) --> Demo Router Agent
    • Known church number (in PHONE_REGISTRY) --> ("church", church_id) --> Church Agent
    • Unknown number (not in registry) --> ("church", None) --> needs DB lookup
  2. If the route is "church" but church_id is None: a. Query church_voice_agents table where twilio_phone_number = dialed number (cached 5 min). b. If found, proceed with that church_id. c. If not found, fall back to Sales Agent.

3. Sales Agent Path

  1. If agent_type is "sales": a. Initialize Supabase client (singleton, lazy). b. Insert a call log row into voice_call_logs with status "in_progress". c. Load in parallel:
    • Product knowledge from product_knowledge table (cached 15 min)
    • Demo church data for Grace Community (protestant demo)
    • Demo church data for St. Joseph Catholic Parish (catholic demo) d. Build the Sales Agent with product knowledge and demo church data. e. Initialize session state (abuse_count=0, crisis_detected=false). f. Return the agent (moderation + noise filtering run via on_user_turn_completed callback).

4. Demo Router Path

  1. If agent_type is "demo_router": a. Insert call log row. b. Load both demo churches in parallel. c. Build Demo Router Agent (offers caller a choice of demo churches to experience). d. Return the agent.

5. Church Agent Path

  1. If agent_type is "church": a. Load church data from church_voice_agents via load_church_data().

    • This includes call-limit enforcement — if the church has exceeded their monthly limit, load_church_data() returns None. b. If church data is None (over limit or load failure), fall back to Sales Agent. c. Insert call log row into voice_call_logs. d. Increment calls_this_month on church_voice_agents (non-fatal if fails).
  2. Load contextual data in parallel (4 concurrent queries):

    • RAG context — denomination-specific theological content from unified_rag_content
    • Product knowledge — from product_knowledge table (cached 15 min)
    • Inline FAQs — church-specific Q&A pairs from church_knowledge_base (cached 5 min)
    • Repeat caller history — last 5 calls from this phone+church pair within 90 days, from voice_call_logs (privacy-gated: agent must NOT proactively mention previous topics)
  3. Build datetime context:

    • Current date/time in the church's timezone
    • "This Sunday" = next Sunday's date
    • "Next week" = the Monday after this Sunday
    • Used so the agent can correctly interpret relative time expressions from callers
  4. Combine all context blocks into one string (RAG + product knowledge + FAQs + repeat history + datetime).

  5. Build the Coordinator Agent with church data and combined RAG context.

    • The Coordinator handles general inquiries, directions, service times, visitor info, giving/tithing.
    • It can hand off to the Care Agent for pastoral/emotional topics (prayer, grief, crisis).
    • LLM: Gemini 2.5 Flash (Coordinator), Claude Haiku 4.5 (Care Agent — better empathy).
    • See products/voice-agent/fallback-strategy.md for fallback plans.
  6. Attach call context to agent (_call_context dict):

    • call_id, caller_phone, dialed_number
    • church_data: the loaded church data dict
    • supabase: async Supabase client
    • care_voice_id: opposite gender voice for Care Agent handoff
  7. Return the Coordinator Agent. Moderation, noise filtering, RAG, and farewell detection run per-turn via on_user_turn_completed callbacks (see voice-turn-processing.md).

6. Per-Turn Processing Loop

For every utterance the caller speaks, the TurnProcessor pipeline runs:

  1. Session injection — caller phone, church data, Supabase client, PCO keys, Cal.com keys, and timezone are injected into the turn environment so tools can access them.

  2. Duration tracking — elapsed time since call start is updated on every turn.

  3. "Are you there?" reassurance — If the agent is currently processing a prior turn and the caller says something like "are you there?" or "hello?", immediately respond "Yes, I'm here! Just one more moment." without interrupting the pending work.

  4. Moderation checks (run BEFORE noise filtering — threats and crises must never be silently dropped):

    a. Threat detection (check_threat) — pattern-matched against violence-toward-others phrases. Has negation guard ("I'm NOT going to kill") and self-harm exclusion (redirects to crisis, not threat).

    • If threat detected:
      • Log moderation violation to DB
      • Send threat alert email to church notification_email
      • Send threat alert SMS to church notification_phone
      • Send threat alert to support team
      • Speak: "I need to stop you right there. This call is being recorded and logged. I'm ending this call now."
      • Wait 4 seconds for audio to finish, then hang up

    b. Crisis detection (check_crisis) — pattern-matched against self-harm, suicidal ideation, domestic violence phrases.

    • If crisis detected:
      • Log moderation violation
      • Send crisis alerts (email + SMS + support)
      • Set crisis_detected = true on session
      • Inject directive into LLM context: "Provide 988 Lifeline immediately. Do NOT ask clarifying questions. Do NOT end the call."

    c. Abuse detection (check_abuse) — profanity, slurs, sexual content.

    • Tracks abuse_count across turns
    • First offense: inject "redirect calmly" context into LLM
    • After threshold: speak "I'm going to end this call now. Have a good day." then hang up
  5. Noise filtering (runs AFTER moderation — only if moderation did not flag the utterance):

    • Silently drop pure noise sounds ("um", "uh", "hmm")
    • Silently drop pure backchannels ("uh huh", "mhm", "i see")
    • Drop context-dependent words ("okay", "yeah", "sure") ONLY if the agent did not ask a question
    • Never filter floor-takes ("wait", "stop", "actually") or farewells ("thanks", "bye")
  6. Per-turn RAG — if moderation did not fire, do a quick semantic search against the church's knowledge base with a 500ms timeout. Adds relevant context to this specific turn.

  7. Combine contexts — merge any moderation directive + per-turn RAG into a single context string.

  8. LLM processing — delegate to the wrapped LlmAgent with the combined context:

    • Mark session as is_processing = true
    • If the LLM wants to call a tool, inject a random filler phrase first ("One moment.", "Let me check on that.", etc.) so the caller hears something while the tool executes. Tools that skip the filler: end_call, demo_agent.
    • Track the agent's last response text for farewell detection.
    • Track whether the agent's response contained a question mark (for noise filtering on the next turn).
    • Set is_processing = false when done.
  9. Tool execution — during LLM processing, the agent may invoke tools:

    • submit_prayer_request — writes to voice_prayer_requests table, sends notification
    • request_callback — writes to voice_callback_requests table, sends notification
    • capture_visitor_contact — writes to voice_visitor_contacts table
    • register_for_event — captures event interest
    • send_giving_link — sends giving URL via SMS
    • send_sms_link — sends a link via SMS to the caller
    • send_directions_link — sends Google Maps link via SMS
    • check_availability / book_appointment — Cal.com integration
    • get_service_times_tool / get_upcoming_events_tool / get_staff_directory_tool — Planning Center integration
    • end_call — terminates the call
  10. Auto-hangup on mutual farewell — after the LLM generates its response:

    • If crisis_detected is true, skip farewell detection entirely (never hang up on someone in crisis).
    • If both the caller's message AND the agent's response are farewells, set a 4-second grace period.
    • If the caller speaks again during the grace period, cancel the hangup.
    • If the grace period expires without new speech, hang up.

7. Call Ends — Data Capture

  1. When a CallEnded event fires (caller hung up, agent hung up, or network drop):

    a. Write call log — update the voice_call_logs row:

    • Set status = "completed"
    • Set duration_seconds = elapsed time since call start
    • Save full transcript (all user + agent messages as JSONB array)
    • Summary starts as empty string (will be filled by classification)

    b. Generate call classification (fire-and-forget via Gemini 2.5 Flash):

    • Only runs if transcript has >50 characters of content
    • Produces 7 structured fields:
      • summary — 1-2 sentence factual summary
      • caller_sentiment — -1.0 to 1.0 scale
      • call_topics — array of topic tags (prayer, visitor, giving, callback, etc.)
      • category — single primary category (prayer_request, visitor, callback_request, pastoral_care, crisis, etc.)
      • urgency — low / normal / urgent / pastoral_emergency
      • follow_up_needed — true/false
      • suggested_assignee — pastor / office_admin / prayer_team / care_team / etc.
    • Updates the voice_call_logs row with these fields

    c. The call is now fully captured. Church admin can view the call in the admin dashboard under the Calls tab, including transcript, summary, classification, and any tool results (prayer requests, callbacks, visitor contacts).