Skip to main content

Voice Clients — Master Config

Per-tenant YAML configuration files for all WiseAI voice + chat agents. This directory is the git-versioned single source of truth for each customer's agent configuration.

Directory structure

knowledge/voice-clients/
_schema.yaml ← JSON Schema draft-07 for all per-tenant YAMLs
_defaults/
church.yaml ← Church vertical defaults (floor values)
funeral.yaml ← Funeral vertical defaults
vet.yaml ← (future) Vet vertical defaults
church/
medhanialem.yaml ← Medhanialem Ethiopian Evangelical Church (real customer)
melvindale-cog.yaml ← Melvindale Church of God (real customer)
hope-community.yaml ← Hope Community Church
churchwiseai-demo.yaml ← Demo church (UUID 00000000-...)
funeral/
walker-mortuary-daughenb-e6c1.yaml ← Walker Mortuary & Daughenbaugh (founder demo)

Cascade order

Values are merged in this order (higher overrides lower):

1. Built-in code defaults (TypeScript: defaultOperationalHandoff / defaultSafetyConfig)
2. _defaults/<vertical>.yaml ← vertical floor values
3. <vertical>/<slug>.yaml ← per-tenant overrides (this directory)
4. tenant_voice_agents DB row ← live tweaks via UI (highest precedence)

Lean YAML principle: only store fields that DIVERGE from _defaults/<vertical>.yaml. A YAML file with only tenant_id, vertical, and display_name is valid — all other fields inherit from the vertical default.

Secrets never go in YAML: cal_api_key, pco_secret, Stripe keys, etc. stay in the DB only. The Export to YAML button in the UI omits them. YAML carries null placeholders for integration fields.

Sync command: pnpm sync-voice-clients

# Validate all YAMLs against _schema.yaml (CI gate — no DB writes)
pnpm sync-voice-clients --check

# Show what WOULD change if pushed (read-only)
pnpm sync-voice-clients --dry-run

# Push all YAMLs to tenant_voice_agents (upsert)
pnpm sync-voice-clients

# Push only funeral vertical
pnpm sync-voice-clients --vertical=funeral

# Push one specific tenant
pnpm sync-voice-clients --tenant=melvindale-cog

# Pull a DB row back to YAML (overwrite file)
pnpm sync-voice-clients --tenant=melvindale-cog --pull

The script reads SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY from .env.local. It cascades _defaults/<vertical>.yaml<vertical>/<slug>.yaml before validating and before computing the DB diff.

Dry-run after seeding should show zero diffs. If diffs appear, the seed has bugs.

Master Config UI

Founder-only UI at /founder/[token]/voice-clients:

  • List view: all tenants sorted by status (active first) → vertical → slug. Each row shows transfer_enabled badge, notification_phone, last_call_at.
  • Edit view: /founder/[token]/voice-clients/[id]/edit — grouped form for all editable fields. Fields unset by the tenant show an "inherited from default" badge. On save, writes to tenant_voice_agents.
  • Export to YAML: downloads the per-tenant YAML (DB state → YAML format) for the founder to commit to git. This is the --pull mode equivalent.
  • Reset to defaults: clears per-tenant overrides (nullifies non-identity columns).

Inbound phone numbers are read-only in the edit view. They're provisioned via POST /api/admin/provision-number (Telnyx auto-buy + LiveKit trunk/dispatch setup).

How to provision a new tenant

Step 1 — Buy a phone number (Telnyx auto-provision)

curl -X POST https://churchwiseai.com/api/admin/provision-number \
-H "Content-Type: application/json" \
-d '{
"token": "$FOUNDER_TOKEN",
"churchId": "<church_uuid_from_premium_churches>",
"areaCode": "416",
"countryCode": "CA"
}'

This creates a LiveKit SIP trunk + dispatch rule and updates the tenant_voice_agents row with the new phone number, trunk ID, and dispatch rule ID.

Step 2 — Create the YAML file

# knowledge/voice-clients/<vertical>/<slug>.yaml
# Source row last updated: 2026-04-28T00:00:00Z

tenant_id: <uuid> # from tenant_voice_agents.tenant_id
vertical: church # or funeral, vet, etc.
display_name: My Church

notification_email: office@mychurch.org
notification_phone: "+1416XXXXXXX"

# Only include fields that DIVERGE from _defaults/church.yaml
operational_handoff:
response_time_promise_text: "within 30 minutes"

inbound_numbers:
- "+1416XXXXXXX"

status: active

Step 3 — Push to DB

pnpm sync-voice-clients --tenant=<slug> --dry-run # verify diff looks right
pnpm sync-voice-clients --tenant=<slug> # push

Step 4 — Set operational overrides via UI

Visit /founder/[token]/voice-clients/<tenant_id>/edit to set:

  • transfer_target_number (director's direct line)
  • transfer_enabled: true (when the tenant is ready for live transfer)
  • escalation_contact_name (e.g. "Charlie, the on-call director")

Export to YAML when satisfied → commit the diff to git.

Two-track escalation rules

TrackTypeToolRoutes toDB table
AOperational handofftransfer_to_director / request_callbacknotification_phone + notification_emailvoice_callback_requests
BSafety/Crisisflag_safety_event988/911/DV (spoken) + support@churchwiseai.com + optional crisis_contact_phone SMScrisis_events

Crisis events NEVER live-bridge a human. crisis_contact_phone receives a post-event SMS only — it is intentionally different from notification_phone. Most tenants leave crisis_contact_phone: null.

Schema parity contract

The YAML schema (_schema.yaml) is the shared contract between:

  • src/lib/tenant-config.ts — TypeScript web runtime (chatbot / admin UI)
  • voice-agent-livekit/core/tenant_config.py — Python voice runtime (Pydantic V2)
  • scripts/sync-voice-clients.ts — YAML ↔ DB sync tool

When you add a new field:

  1. Add to _schema.yaml
  2. Add to both tenant-config.ts + tenant_config.py
  3. Add a DB migration for the new column in tenant_voice_agents
  4. Update _defaults/<vertical>.yaml with the default value

Adding a new vertical

  1. Add the vertical key to _schema.yamlvertical.enum
  2. Create _defaults/<vertical>.yaml with sensible defaults
  3. Create src/lib/verticals/<vertical>.ts (VerticalProfile)
  4. Create voice-agent-livekit/verticals/<vertical>/prompts.py + tools.py
  5. Add <vertical>/ directory here and seed the first tenant YAML
  6. Run pnpm sync-voice-clients --vertical=<vertical> to push

See knowledge/runbooks/voice-vertical-template.md for the full walkthrough.