Skip to content

Voice Agent Configuration

Vapi.ai Squad Architecture — v2.3.0 (Production) & v3.0 (Deployed)


Version History

Version Status Architecture Assistants Languages Squad ID
v2.3.0 Released 6-agent multilingual squad 6 Auto-detect (EN/ZH) 775db28c-...
v3.0 Deployed (phone-assigned, testing) 9-agent dual-track EN/ZH 9 Explicit language gate 13fdfd19-...

v3.0 — Dual-Track Bilingual Squad

Overview

Setting Value
Architecture 9-Agent Dual-Track (Router + 4 roles x 2 languages)
Squad ID 13fdfd19-a2cd-4ca4-8e14-ad2275095e32
LLM OpenAI GPT-4o (all 9 assistants)
Temperature 0.3 (Router), 0.5 (all others)
Max Tokens 150 (Router), 200 (standard), 250 (Registration)
Handoff Mode Silent (no announcement)
Config Management Vapi GitOps (config-as-code)
GitOps Path vitara-platform/vapi-gitops/
Webhook https://api-dev.vitaravox.ca/api/vapi/*

Key Design Changes from v2.3.0

Change v2.3.0 v3.0 Rationale
Language handling Single multilingual agent Explicit Router language gate Eliminates LLM language confusion; dedicated STT/TTS per track
Patient identification Combined in Router Separate Patient-ID agent per track Cleaner prompt; Router stays lightweight
Reschedule + Cancel Two separate agents Single Modification agent per track Reduces squad complexity; both share same tools
Confirmation agent Exists (rarely used) Eliminated log_call_metadata absorbed into Booking/Modification/Registration
STT (English) Deepgram nova-2 multi Deepgram nova-2 en Language-specific = higher accuracy
STT (Chinese) Deepgram nova-2 multi Deepgram nova-2 zh Language-specific = higher accuracy
STT (Router) Deepgram nova-2 multi AssemblyAI Universal Bilingual detection before routing
TTS (English) ElevenLabs multilingual_v2 ElevenLabs multilingual_v2 No change
TTS (Chinese) ElevenLabs multilingual_v2 Azure zh-CN-XiaoxiaoNeural Native Mandarin voice quality
ZH Endpointing Same as EN Longer pauses (1.0s wait, 0.6s punct) Mandarin speech patterns need longer pauses

v3.0 Squad Members

# Agent Vapi Name ID Role Tools
1 Router vitara-router-v3 4f70e214-... Language gate: detect EN/ZH, route to correct track get_clinic_info, transfer_call, log_call_metadata
2 Patient-ID (EN) vitara-patient-id-en-v3 7d054785-... Identify caller by phone, detect intent, route search_patient_by_phone, search_patient, get_clinic_info, transfer_call
3 Patient-ID (ZH) vitara-patient-id-zh-v3 7585c092-... Same as EN but in Mandarin Same as EN
4 Booking (EN) vitara-booking-en-v3 ac25775b-... Find slots, book appointments find_earliest_appointment, check_appointments, create_appointment, get_providers, log_call_metadata
5 Booking (ZH) vitara-booking-zh-v3 6ef04a40-... Same as EN but in Mandarin Same as EN
6 Modification (EN) vitara-modification-en-v3 9cd8381d-... Reschedule, cancel, check appointments check_appointments, find_earliest_appointment, update_appointment, cancel_appointment, create_appointment, get_providers, log_call_metadata, transfer_call
7 Modification (ZH) vitara-modification-zh-v3 e348cd2f-... Same as EN but in Mandarin Same as EN
8 Registration (EN) vitara-registration-en-v3 9fcfd00d-... Register new patients register_new_patient, add_to_waitlist, log_call_metadata
9 Registration (ZH) vitara-registration-zh-v3 ce50df43-... Same as EN but in Mandarin Same as EN

v3.0 Call Flow

CALL START (Telnyx inbound)
    |
    v
+--------------------------------------------------+
| 1. ROUTER (vitara-router-v3)                      |
|    STT: AssemblyAI Universal (bilingual)          |
|    TTS: ElevenLabs multilingual_v2                |
|                                                    |
|    - System greeting plays: "Hi, this is Vitara   |
|      Clinic. How can I help you?"                 |
|    - Wait for caller to speak                     |
|    - Detect language from first utterance          |
|    - Route immediately to language track           |
+---------------------+----------------------------+
                      |
          +-----------+-----------+
          v                       v
    ENGLISH TRACK            CHINESE TRACK
          |                       |
          v                       v
+-------------------+   +-------------------+
| 2. PATIENT-ID-EN  |   | 2. PATIENT-ID-ZH  |
|  STT: Deepgram    |   |  STT: Deepgram    |
|       nova-2 en   |   |       nova-2 zh   |
|  TTS: ElevenLabs  |   |  TTS: Azure       |
|                   |   |    XiaoxiaoNeural  |
| - search by phone |   | - search by phone |
| - detect intent   |   | - detect intent   |
| - route to role   |   | - route to role   |
+--------+----------+   +--------+----------+
         |                        |
  +------+------+         +------+------+
  v      v      v         v      v      v
+----+ +----+ +-----+  +----+ +----+ +-----+
|BOOK| |MOD | |REG  |  |BOOK| |MOD | |REG  |
| EN | | EN | | EN  |  | ZH | | ZH | | ZH  |
+----+ +----+ +-----+  +----+ +----+ +-----+

v3.0 Handoff Matrix (20 Routes)

From To Trigger
Router Patient-ID-EN Caller speaks English
Router Patient-ID-ZH Caller speaks Chinese
Patient-ID-EN Booking-EN Patient found + intent = book
Patient-ID-EN Modification-EN Patient found + intent = reschedule/cancel/check
Patient-ID-EN Registration-EN Patient not found + new patient
Patient-ID-ZH Booking-ZH Patient found + intent = book
Patient-ID-ZH Modification-ZH Patient found + intent = reschedule/cancel/check
Patient-ID-ZH Registration-ZH Patient not found + new patient
Booking-EN Modification-EN Patient says "reschedule" or "cancel"
Booking-EN Router Unrelated request
Booking-ZH Modification-ZH Patient says "改时间" or "取消"
Booking-ZH Router Unrelated request
Modification-EN Booking-EN After cancel, patient wants to rebook
Modification-EN Router Unrelated request
Modification-ZH Booking-ZH After cancel, patient wants to rebook
Modification-ZH Router Unrelated request
Registration-EN Booking-EN After registration, patient wants first appointment
Registration-ZH Booking-ZH After registration, patient wants first appointment
Any EN agent transfer_call Error fallback / frustrated caller
Any ZH agent transfer_call Error fallback / frustrated caller

v3.0 Voice & Transcription Configuration

English Track

Setting Value
STT Provider Deepgram
STT Model nova-2
STT Language en
TTS Provider ElevenLabs
TTS Voice ID fQj4gJSexpu8RDE2Ii5m
TTS Model eleven_multilingual_v2
Stability 0.5
Similarity Boost 0.7

Endpointing (EN standard agents):

Setting Value
Wait Seconds 0.6
On Punctuation 0.3s
On No Punctuation 0.8s
On Number 0.5s
Stop on Words 2

Endpointing (EN registration — longer pauses for spelling):

Setting Value
Wait Seconds 1.6
On Punctuation 1.0s
On No Punctuation 2.5s
On Number 1.2s
Stop on Words 2

Chinese Track

Setting Value
STT Provider Deepgram
STT Model nova-2
STT Language zh
TTS Provider Azure
TTS Voice ID zh-CN-XiaoxiaoNeural

Endpointing (ZH standard agents — tuned for Mandarin speech patterns):

Setting Value
Wait Seconds 1.0
On Punctuation 0.6s
On No Punctuation 1.5s
On Number 0.8s
Stop on Words 3

Endpointing (ZH registration — longer pauses for spelling):

Setting Value
Wait Seconds 1.6
On Punctuation 1.0s
On No Punctuation 2.5s
On Number 1.2s
Stop on Words 2

Router

Setting Value
STT Provider AssemblyAI
STT Mode Universal (bilingual auto-detect)
TTS Provider ElevenLabs
TTS Voice ID fQj4gJSexpu8RDE2Ii5m
TTS Model eleven_multilingual_v2

v3.0 Agent Behaviors

Router (Language Gate)

  • On call start: System greeting plays ("Hi, this is Vitara Clinic. How can I help you?")
  • Does NOT: Look up patients, ask for details, perform booking
  • ONLY JOB: Detect language from first utterance, route to correct track
  • English detected: "One moment please." -> Patient-ID-EN
  • Chinese detected: "请稍等。" -> Patient-ID-ZH
  • Clinic info shortcut: If caller asks for hours/address, call get_clinic_info and answer directly
  • Emergency: Detects keywords in both EN and ZH, responds in detected language, ends call
  • Fallback: After 3 unclear attempts, transfer to staff

Patient-ID (EN/ZH)

  • On handoff: Does NOT re-greet. Waits for caller to speak, analyzes intent
  • Phone lookup: Calls search_patient_by_phone with phone: "0000000000" (server substitutes real number)
  • Found + intent known: "Hi [Name]!" then route immediately (no "how can I help")
  • Found + intent unknown: "Hi [Name], how can I help you today?"
  • Not found: "Are you a new patient?" -> Registration or manual search
  • On behalf of: Supports "calling for my husband/child" flow
  • Routes to: Booking, Modification, Registration, or answers clinic info directly

Booking (EN/ZH)

  • First turn: Immediately calls find_earliest_appointment with NO filters
  • Presents: One slot at a time ("How about [day] at [time] with Dr. [name]?")
  • Asks reason: Maps to appointmentType (B=general, 2=follow-up, 3=complaint, P=prescription)
  • Books: Calls create_appointment with demographicId, providerId, startTime, appointmentType, reason, language
  • Never says "booked" without calling the tool first
  • Post-booking: Confirms details, calls log_call_metadata, reminds about health card
  • Wrong intent: Redirects to Modification or Router

Modification (EN/ZH)

  • Consolidates: Reschedule + Cancel + Check into one agent
  • First: Calls check_appointments to find existing appointments
  • Multiple found: Lists briefly, asks which one
  • Reschedule: Calls find_earliest_appointment then update_appointment
  • Cancel: Confirms, calls cancel_appointment, offers rebooking
  • Check only: Reads appointment details, asks if changes needed
  • Never says "moved/cancelled" without calling the tool first
  • Post-action: Calls log_call_metadata with outcome

Registration (EN/ZH)

  • Collects 7 fields one at a time: name, gender, DOB, phone, address, health card, email
  • Spelling: Uses NATO-style phonetic confirmation ("A as in Apple")
  • Silent during spelling: Does NOT acknowledge individual letters
  • Health card types: BC (10-digit PHN), OUT_OF_PROVINCE, PRIVATE
  • Confirms all before calling register_new_patient
  • Post-registration: Offers first appointment booking (routes to Booking)
  • Not accepting: Offers waitlist via add_to_waitlist

v3.0 Tool Inventory (14 Tools)

Tool Slug Vapi ID Used By
search_patient_by_phone search-patient-by-phone-8474536c 8474536c-... Patient-ID
search_patient search-patient-4889f4e5 4889f4e5-... Patient-ID
get_patient get-patient-d86dee47 d86dee47-... (reserved)
get_clinic_info get-clinic-info-aaec50cf aaec50cf-... Router, Patient-ID
get_providers get-providers-1ffa2c33 1ffa2c33-... Booking, Modification
find_earliest_appointment find-earliest-appointment-7fc7534d 7fc7534d-... Booking, Modification
check_appointments check-appointments-74246333 74246333-... Booking, Modification
create_appointment create-appointment-65213356 65213356-... Booking, Modification
update_appointment update-appointment-635f59ef 635f59ef-... Modification
cancel_appointment cancel-appointment-f6cef2e7 f6cef2e7-... Modification
register_new_patient register-new-patient-9a888e09 9a888e09-... Registration
add_to_waitlist add-to-waitlist-0153bac0 0153bac0-... Registration
log_call_metadata log-call-metadata-4619b3cb 4619b3cb-... All (except Patient-ID)
transfer_call transfer-call-d95ed81e d95ed81e-... Router, Patient-ID, Modification

Tool Distribution Matrix (v3.0)

Tool Router Patient-ID Booking Modification Registration
search_patient_by_phone EN/ZH
search_patient EN/ZH
get_clinic_info EN/ZH EN/ZH
get_providers EN/ZH EN/ZH
find_earliest_appointment EN/ZH EN/ZH
check_appointments EN/ZH EN/ZH
create_appointment EN/ZH EN/ZH
update_appointment EN/ZH
cancel_appointment EN/ZH
register_new_patient EN/ZH
add_to_waitlist EN/ZH
log_call_metadata EN/ZH EN/ZH EN/ZH EN/ZH
transfer_call EN/ZH EN/ZH EN/ZH

v3.0 Global Behaviors (All Agents)

  • Emergency detection: Both EN and ZH keywords trigger 911 message in caller's language
  • Silent transfers: No agent ever mentions "transferring", "assistant", or system internals
  • Date awareness: All prompts include {{now | date: ...}} template for current date/time
  • Past-date clamping: Server rejects past dates and clamps to today
  • Phone auto-detection: Server extracts real caller phone from call.customer.number
  • One question at a time: Never overwhelm the caller
  • Delay handling: "Just one moment..." / "请稍等..." if tool takes >3 seconds
  • Never say technical terms: No "function", "tool", "API", "database"

v3.0 Appointment Type Mapping

Patient Says (EN) Patient Says (ZH) Code Meaning
"checkup", "general", "physical", "not sure" "体检", "普通检查", "不确定" B General visit
"follow-up", "results", "check results" "复查", "看结果" 2 Follow-up
"pain", "illness", "specific complaint" "不舒服", "疼痛", "生病" 3 Specific concern
"prescription refill", "medication" "配药", "续药" P Prescription

Server-side validation: appointmentType must be one of ['B', '2', '3', 'P']. Invalid values default to 'B'.


v3.0 Server-Side Middleware Changes

Call Metadata Cache

v3.0 introduced an in-memory callMetadataCache to bridge tool-call webhooks and end-of-call-report webhooks:

Tool call (log_call_metadata)    End-of-call-report
        |                               |
        v                               v
  setCallMetadata(callId, {       getCallMetadata(callId)
    language, outcome,              -> merges into saveCallLog
    demographicId, appointmentId  })
  • Why: Vapi's end-of-call-report doesn't include tool results. The cache lets the server persist metadata that was set during the call.
  • Fallback: create_appointment and register_new_patient also cache language and demographicId as a safety net if log_call_metadata is never called.
  • TTL: Cache entries auto-expire (in-memory Map, cleared on process restart).

Clinic Resolution Chain

Incoming webhooks resolve the clinic via this chain:

metadata.clinicId  ->  assistantId lookup  ->  squadId lookup  ->  phoneNumber lookup

v3.0 added vapi_squad_id_v3 column to clinic_config so both v2.3.0 and v3.0 squads resolve to the same clinic:

SELECT clinic_id FROM clinic_config
WHERE vapi_squad_id = $1 OR vapi_squad_id_v3 = $1
LIMIT 1

v2.3.0 — Production Squad (6 Agents)

Overview

Setting Value
Architecture 6-Agent Squad
Squad ID 775db28c-21cf-4eec-a643-d078cf9bc5c1
LLM OpenAI GPT-4o
Temperature 0.5
Languages English, Mandarin, Cantonese, French, Punjabi (auto-detect)
Handoff Mode Silent (no announcement)
Transcriber AssemblyAI Universal Multilingual
TTS ElevenLabs eleven_multilingual_v2, voice fQj4gJSexpu8RDE2Ii5m
Webhook https://api-dev.vitaravox.ca/api/vapi/*

v2.3.0 Squad Members

Agent Vapi Name ID Role Tools
Router vitara-router-v2 45f98810-... Greeting, phone auto-ID, intent routing search_patient_by_phone, get_clinic_info, transfer_call
Booking vitara-booking-v2 e5ad78c3-... Find slots, book appointments find_earliest_appointment, create_appointment, get_providers, transfer_call
Reschedule vitara-reschedule-v2 c1e6a6d8-... View/modify existing appointments check_appointments, find_earliest_appointment, cancel_appointment, create_appointment
Cancel vitara-cancel-v2 0d2f2b38-... Cancel appointments check_appointments, cancel_appointment, transfer_call
Registration vitara-registration-v2 f8e68875-... New patient registration get_clinic_info, register_new_patient, add_to_waitlist
Confirmation vitara-confirmation-v2 e1d5d83a-... Wrap-up (rarely used) log_call_metadata

v2.3.0 Key Design Decisions

  1. Phone auto-detection: Router calls search_patient_by_phone on call start. Server extracts real caller phone from call.customer.number, ignoring LLM hallucinations.

  2. Booking-first flow: Booking assistant immediately searches for earliest available slot with NO filters. Preferences (doctor, date, time) applied only on request.

  3. Single-slot returns: find_earliest_appointment returns exactly 1 slot per call. Rejected slots added to excludeDates for next search.

  4. Inline confirmation: All assistants handle their own confirmation and goodbye. Confirmation agent exists but is rarely used.

  5. Silent transfers: No assistant mentions "transferring" or other assistant names.

  6. Single multilingual agent: All agents handle EN/ZH/FR/PA via auto-detection (one prompt per agent, not per language).

v2.3.0 Call Flow

CALL START
    |
    v
+-------------------------------+
| 1. ROUTER                      |
| - Greeting                     |
| - search_patient_by_phone      |
| - Emergency/frustration detect |
+---------------+---------------+
                |
    +-----------+------+-------+
    v           v      v       v
+--------+ +------+ +------+ +------+
| BOOKING| |RESCHED| |CANCEL| |REGIST|
+--------+ +------+ +------+ +------+
                                 |
                                 v
                          +------------+
                          |CONFIRMATION|
                          | (fallback) |
                          +------------+

v2.3.0 Voice Configuration

Setting Value
STT Provider AssemblyAI Universal Multilingual
TTS Provider ElevenLabs
TTS Voice ID fQj4gJSexpu8RDE2Ii5m
TTS Model eleven_multilingual_v2
Silence Timeout 30 seconds
Max Duration 600 seconds (10 min)

v2.3.0 Member Destinations Matrix

From To Trigger
Router Booking Intent = book
Router Reschedule Intent = reschedule
Router Cancel Intent = cancel
Router Registration Patient not found
Registration Booking After reg, wants appointment
Booking Router Booking complete
Reschedule Booking After cancel, wants to rebook
Cancel Booking After cancel, wants to rebook

Comparison: v2.3.0 vs v3.0

Feature v2.3.0 v3.0
Agents 6 9
Language approach Auto-detect in each agent Explicit Router language gate
Patient identification In Router agent Separate Patient-ID agent
Reschedule + Cancel 2 separate agents 1 Modification agent
Confirmation agent Exists (rarely used) Eliminated (absorbed)
STT AssemblyAI Universal (all) AssemblyAI (Router) + Deepgram per-language
TTS (Chinese) ElevenLabs multilingual Azure XiaoxiaoNeural
ZH endpointing Same as EN Tuned for Mandarin (longer pauses)
Config management Manual Vapi dashboard Vapi GitOps (config-as-code)
log_call_metadata On Confirmation agent On every role agent
Clinic info On Router On Router + Patient-ID
Handoff routes 8 20

Testing

Test v3.0 Squad Flow

# 1. Open Vapi Dashboard -> Squads -> vitaravox-v3
# 2. Click "Test" to start a web call
# 3. Router greeting plays, say "I'd like to book"
# 4. Patient-ID-EN identifies you, routes to Booking-EN
# 5. Booking finds slot, you confirm
# 6. Verify in OSCAR EMR

# Chinese track:
# 3b. Say "我想预约" (I want to book)
# 4b. Patient-ID-ZH identifies, routes to Booking-ZH

Test Webhook Tools

# Test patient search
curl -X POST https://api-dev.vitaravox.ca/api/vapi/search-patient-by-phone \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $VAPI_SECRET" \
  -d '{"phone": "+16045551234"}'

# Test clinic info
curl -X POST https://api-dev.vitaravox.ca/api/vapi/get-clinic-info \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $VAPI_SECRET" \
  -d '{}'

Test Language Switching

1. Start in English: "I'd like to book an appointment"
2. Switch to Mandarin mid-call: "我想预约下周三"
3. In v2.3.0: Agent adapts seamlessly (auto-detect)
4. In v3.0: Router detects initial language, routes to correct track
   (mid-call language switch stays in current track)