Skip to main content

FuneralWiseAI — Live Director Transfer

Why now (pre-cold-email)

Founder's strategic frame: to compete with ASD Answering Service (~33% of US funeral homes, $150-300/mo, human-pickup) on the day-side, FuneralWiseAI needs real-time human handoff, not just async callback. "Funeral homes would want to get contacted, day or night or else they would lose business." Async callback is a fine secondary path; live transfer is the primary path.

We were about to send the cold-email batch with async-only escalation. Founder decided to delay the batch ~1 week and ship live transfer first. Better demo, stronger product, fewer "but what if I'm not at my desk" objections.

The killer demo (founder's idea — 2026-04-28)

"Could you imagine if we let the client put in their phone number and use browser call in and see the handoff to themselves personally and get a phone call?"

Mechanic: prospect lands on funeralwiseai.com/s/<their-funeral-home-slug>, types their own cell number into a "configure your director phone" field, clicks "Call from your browser." They have a normal at-need-style conversation with the AI as if they're a grieving family. At some point they say "can I speak with a director?" — the AI fires the transfer_to_director tool. Their own phone rings. They answer. They are NOW in a 3-way call: themselves-as-family + AI agent + themselves-as-director. They experience the handoff first-hand AND see how it would work for their own customers.

This is unforgettable. Sales-cycle compressor. Build the demo flip into /s/[slug] as part of the v1 ship.

Current state (what we already have)

Async escalation works: request_callback tool fires on at-need death, writes to voice_callback_requests with urgency, sends SMS + email to notification_phone/notification_email. Verified working today (call_sid web-browser-walker-mortuary-daughenb-e6c1-moitecum, callback badc4909...). ✅ Per-tenant config exists: tenant_voice_agents has notification_phone, notification_email, escalation_contact_name, pastor_availability_text (or funeral equivalent). ✅ Prompt scaffolding for HUMAN ESCALATION already lives in verticals/funeral/prompts.py:_build_human_escalation(). Defines WHEN the agent should escalate. ✅ LiveKit SIP outbound supported at the SDK level — lk_api.SIP.create_sip_participant() initiates outbound dial, when target answers they're added to the room.

What we DON'T have (to build)

Outbound SIP trunk — current ST_Xa3Bp9aixRFP is INBOUND-locked per CLAUDE.md. Need a separate outbound trunk via Telnyx/Twilio. Per-minute cost ~$0.005-0.01 US. ❌ transfer_to_director tool — agent-callable function that initiates the dial-out + bridges into the room. ❌ Fallback logic — if director doesn't answer within N seconds, gracefully degrade to request_callback + verbal "we'll call you back within {response_time_promise}." ❌ response_time_promise per-tenant config — agent currently says generic "very soon"; we want "within 15 minutes" / "within the hour" depending on the funeral home's actual SLA. ❌ Demo mechanic UI on /s/[slug] — input field for "use my phone as the director phone for this demo" + token-stamp it onto the LiveKit room metadata so the agent knows which number to dial.

Architecture sketch

New tool: transfer_to_director(reason: str, urgency: str)

1. Agent decides to fire (at-need death, demand for human, etc.)
2. Tool reads tenant_voice_agents.director_phone (validated E.164)
3. Tool calls lk_api.SIP.create_sip_participant():
- sip_trunk_id = TENANT_OUTBOUND_TRUNK_ID (env var)
- sip_call_to = director_phone
- room_name = ctx.room.name (current caller's room)
- participant_identity = f"director-{director_phone}"
4. Wait up to 25s for participant to publish audio (= director answered)
5. ON SUCCESS:
- Agent says one bridge line: "I have John on the line — his grandmother just passed at hospice in Woodstock, Ontario. I'll let you take it from here."
- Agent stops generating turns (room_input_options.close_on_disconnect or
a "step back" signal)
- Caller + director continue 1-on-1; LiveKit handles audio mixing
6. ON TIMEOUT (no answer / busy):
- Fall through to existing request_callback tool (writes DB row,
SMS+email to director)
- Agent tells caller: "I couldn't reach a director directly, but I've
made sure they get your message. Someone will call you back within
{response_time_promise}."
7. ON SIP ERROR (trunk down, invalid number, etc.):
- Same fallback as timeout. Loud structured log [TRANSFER] FAILED
reason=<exc> for diagnostics.

Schema additions (migration on tenant_voice_agents)

ColumnTypeDefaultNotes
director_phonetextNULLE.164 phone for live transfer. NULL = transfer disabled.
transfer_enabledbooleanfalseMaster toggle. False = always async-only.
transfer_business_hours_onlybooleanfalseIf true, only attempt transfer during configured hours; outside → straight to async.
transfer_business_hoursjsonbNULLPer-day windows (Mon-Sat 8am-6pm pattern, etc.) — same shape as existing pastor_availability
response_time_promise_texttext"shortly"Spoken to caller when async fallback fires. e.g. "within 15 minutes" / "within the hour".
transfer_ring_secondsint25How long to wait for director to answer before fallback
transfer_bridge_intro_templatetextNULLOptional override for the agent's bridge line. Falls back to a default template if NULL.

Demo mechanic (/s/[slug] UI)

For the prospect-self-handoff demo, expose a small input on the demo page:

[Test the live director transfer with your own phone number:]
[__________________] [Save and call]

When prospect saves a number:

  1. POST to /api/livekit/token with { prospectSlug, demoOverrides: { directorPhone: "+1xxxxxxxxxx" } }
  2. Token mint stamps demo_director_phone_override into the LiveKit room metadata (separate from prospect_slug)
  3. Voice agent reads override at session start, uses it instead of tenant_voice_agents.director_phone
  4. Limited to one number per session (no re-targeting mid-call)
  5. Number is NOT persisted to tenant_voice_agents — it's session-scoped only

Server-side guards:

  • Rate-limit by IP (3 numbers/day to avoid abuse)
  • E.164 validation
  • Block known disposable / VoIP test numbers? Probably not — false positives. Just rate-limit.
  • Session TTL: ≤30 min to dial out (no calling 2 days later)

Implementation timeline (target: 3 working days)

Day 1 — outbound trunk + base tool

  • Telnyx outbound SIP trunk (or Twilio elastic SIP) provisioned + creds in LiveKit Cloud secrets as TENANT_OUTBOUND_TRUNK_ID
  • Schema migration in churchwiseai-web/migrations/ for the 7 new columns on tenant_voice_agents
  • New transfer_to_director tool in verticals/<future-shared>/tools.py (or temporarily on the church Coordinator class as a hot-patch — Phase 2 generalizes)
  • Tool happy path: dial director, on answer agent says bridge line and stops generating
  • Smoke test via synthetic dispatch with a personal phone number

Day 2 — fallback logic + prompt updates

  • Timeout fallback to request_callback + verbal "within {response_time_promise}"
  • Funeral coordinator prompt updates:
    • Forbid "I'm going to connect you right now" without firing transfer_to_director first
    • Explicit USE-CASE-WHEN-TO-FIRE language for transfer_to_director (at-need death, demand for human, urgent legal/cert questions)
    • Read response_time_promise from home dict and use it verbatim in fallback language
  • Church coordinator prompt: same updates so it benefits too (church demos with director phone configured)
  • Structured logs: [TRANSFER] dialing, [TRANSFER] answered, [TRANSFER] timeout fallback=callback, [TRANSFER] error reason=…
  • Voice deploy + verification

Day 3 — demo mechanic + provisioning UI minimum

  • /s/[slug] demo page input: "test transfer with your own number"
  • /api/livekit/token extension to accept demoOverrides.directorPhone
  • Token stamps demo_director_phone_override into room metadata
  • Voice agent reads override at session start
  • Provisioning UI minimum (founder-only, will fold into Phase 2 Master Config later):
    • /founder/[token]/voice-agents/[id]/transfer admin page
    • Set director_phone, transfer_enabled, response_time_promise_text, business_hours
    • "Send me a test transfer" button that calls the agent's transfer_to_director against the founder's number
  • End-to-end test: prospect → browser call → ask for director → prospect's phone rings → 3-way call works

Risks + mitigations

RiskSeverityMitigation
Outbound SIP costs spiral if abused (especially demo mechanic)MRate-limit demo dial-outs to 3/IP/day. Per-tenant daily transfer cap (e.g. 50/day). Alert on cost > $5/day.
Director doesn't answer (common case)HFallback to async request_callback works today, already tested. The promise becomes "we'll call you back within {response_time}." User experience degrades gracefully.
Spam to director phones via demo mechanicMServer-side validation: E.164 only, rate limit, session TTL ≤30min. Optionally require email verification for the demo flip in v2.
LiveKit SIP outbound fails due to trunk misconfigHSmoke test on day 1 with our own phone numbers. Alarm + auto-fallback in tool. Document the trunk config in knowledge/runbooks/.
Director hears "AI bridge line" and is confusedLBridge line template is per-tenant override-able. Default: warm + concise context. Worst case the director just hears "Hello, this is Walker Mortuary's AI assistant — I have John on the line, his grandmother passed at hospice in Woodstock. Connecting you now."
Caller gets confused when AI fades out and a human appearsMBridge line speaks AT the director-side; caller hears nothing different until director starts talking. Smooth from caller perspective.
Live customer phone lines accidentally enabled for transferHtransfer_enabled defaults FALSE. Existing 4 customer rows stay false until founder opts each one in.

Open questions (resolve in next session's planning before code starts)

  1. Outbound SIP provider — Telnyx (already used for inbound on new customers) or Twilio (existing relationship)? Recommendation: Telnyx for cost + already-integrated; founder confirms.
  2. Trunk ID storage — single shared outbound trunk in LiveKit Cloud secret OUTBOUND_TRUNK_ID, or per-tenant trunks for billing isolation? Recommendation: single shared, with a per-tenant cap; per-tenant only if a customer demands isolation later.
  3. Director-side experience — does the director hear the AI's bridge line, or do we want them to JUST hear the family with no AI intro? Founder preference?
  4. What happens to the AI when the director joins — fades silent / leaves the room / stays as a transcript-capture observer? Recommendation: stays in the room as silent observer for transcript continuity, but does not generate turns. Caller + director see a clean experience.
  5. Demo mechanic abuse mitigation — beyond IP rate limit, do we want the demo flip behind a Cloudflare Turnstile / hCaptcha? Probably yes for the cold-email batch URLs since they're public. Founder approves.
  6. Recording the bridge call — once a human director joins, do we keep recording? US/Canada two-party consent laws vary. Recommendation: stop recording at bridge moment; caller is told via opening disclosure that "this call may be recorded for quality" but live director-bridged calls drop recording. Confirm legal.
  7. Voicemail detection — if the director's voicemail picks up, does that count as "answered"? LiveKit/SIP usually treats it as answered (audio detected). Need post-answer voicemail detection (silence + tone heuristic) OR shorter ring timeout. Recommendation: 25s ring timeout, no post-answer detection in v1; if calls land in voicemail too often, add detection in v2.
  8. Response-time promise variants — what's the canonical list customers can pick? Recommendation: dropdown of {"immediately", "within 5 minutes", "within 15 minutes", "within the hour", "within 2 hours", "within 4 hours", "by end of business day"} — covers funeral (urgent) through church (less urgent).

Sales / competitive positioning

This feature is the lever that makes (b) volume-priced premium + (c) hybrid co-existence from the integrations doc actually work against ASD:

  • Day-side AI handles 80% of operational calls — info, hours, pricing-ballpark, simple callbacks. Cheap.
  • At-need / demand-for-human → live director transfer — the moment of truth. AI doesn't pretend to handle, it bridges to the human in real time. Same UX as ASD's value prop, but at scale.
  • After-hours fallback to ASD coexistence (Phase 2 with FuneralSync 3.0 API) — when the director's phone doesn't answer at 2am, the AI hands the case to ASD via API instead of just a callback row.

Pricing position once shipped: $999 setup + $199/mo + outbound SIP costs (pass-through, ~$5-15/mo for typical use). Frame it: "ASD costs $165/mo to answer. We cost $199/mo to answer + transfer + integrate + chatbot — and if the director doesn't pick up, you get the SMS callback for free."

Phase 2 follow-ups (after v1 ships)

  • Multi-director routing (round-robin, on-call schedule, role-based — "transfer to whoever's on funeral call this week")
  • Voicemail detection (don't fade AI out if the director's voicemail picks up; instead step back into capture mode)
  • Conference mode (3-way: family + AI + director, AI continues to take notes / fire follow-up tools mid-call)
  • ASD FuneralSync 3.0 integration as failover when director also doesn't answer (true 24/7 coverage)
  • Unified TenantConfig.tools whitelist (per the unified architecture doc) — transfer_to_director becomes one tool in the catalog, vertical defaults set it on for funeral / off for transactional
  • Master Config UI exposes transfer config per-tenant (already in scope for Phase 2 provisioning UI)

Decision

Build live transfer in 3 working days, BEFORE the cold-email batch sends. Founder commits to delaying the batch. Plan-then-execute starts next session.