Skip to content

Data Flow & Guardrails

Data Ownership, Security Boundaries, and Conversation Flows


Data Ownership Model

VitaraVox maintains clear separation between its own data and clinic EMR data.

What VitaraVox Owns (Vitara Database)

Data Type Purpose Retention
Clinic profiles Name, address, timezone Permanent
OSCAR credentials Encrypted OAuth tokens Permanent
Working hours Per-day open/close times Permanent
Holidays Closure dates Annual review
Provider display names Voice-friendly names Permanent
Waitlist entries New patient queue Until registered
Call logs Analytics, outcomes 1 year
User accounts Admin dashboard access Permanent
Audit logs Admin action tracking 7 years

What OSCAR Owns (Clinic EMR)

Data Type VitaraVox Access Purpose
Patient demographics Read/Write Lookup, registration
Appointments Read/Write Booking, rescheduling
Provider schedules Read Availability check
Medical records NONE Never accessed
Lab results NONE Never accessed
Prescriptions NONE Never accessed
Billing NONE Never accessed

Data Flow Diagram

┌─────────────────────────────────────────────────────────────────────────────┐
│                          DATA FLOW OVERVIEW                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  INBOUND (Patient → System)                                                 │
│  ─────────────────────────                                                  │
│                                                                              │
│  Voice Input                                                                 │
│      │                                                                       │
│      ▼                                                                       │
│  ┌──────────┐   Transcript    ┌──────────┐   Webhook    ┌──────────┐       │
│  │  Vapi    │ ──────────────▶ │   LLM    │ ───────────▶ │  Vitara  │       │
│  │  (STT)   │                 │ (GPT-4o) │              │Middleware│       │
│  └──────────┘                 └──────────┘              └────┬─────┘       │
│                                                               │             │
│                                   ┌───────────────────────────┤             │
│                                   │                           │             │
│                                   ▼                           ▼             │
│                            ┌──────────┐               ┌──────────┐         │
│                            │  Vitara  │               │  OSCAR   │         │
│                            │    DB    │               │   EMR    │         │
│                            │          │               │          │         │
│                            │ Config   │               │ Patients │         │
│                            │ Logs     │               │ Appts    │         │
│                            └──────────┘               └──────────┘         │
│                                                                              │
│  OUTBOUND (System → Patient)                                                │
│  ───────────────────────────                                                │
│                                                                              │
│  ┌──────────┐   Response     ┌──────────┐   Speech     ┌──────────┐        │
│  │  OSCAR   │ ─────────────▶ │  Vitara  │ ───────────▶ │   Vapi   │        │
│  │   EMR    │                │Middleware│              │  (TTS)   │        │
│  └──────────┘                └──────────┘              └────┬─────┘        │
│                                                              │              │
│                                                              ▼              │
│                                                        Voice Output         │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Mermaid: Data Flow Overview

flowchart TB
    subgraph Inbound["⬇️ INBOUND (Patient → System)"]
        direction LR
        Voice1["🎤 Voice Input"]
        VapiSTT["Vapi<br/>(STT)"]
        LLM2["LLM<br/>(GPT-4o)"]
        Middleware1["Vitara<br/>Middleware"]

        Voice1 -->|"Audio"| VapiSTT
        VapiSTT -->|"Transcript"| LLM2
        LLM2 -->|"Webhook<br/>Tool Call"| Middleware1
    end

    subgraph Storage["💾 Data Storage"]
        VitaraDB2[("Vitara DB<br/>• Config<br/>• Logs<br/>• Audit")]
        OSCAR4[("OSCAR EMR<br/>• Patients<br/>• Appointments")]
    end

    subgraph Outbound["⬆️ OUTBOUND (System → Patient)"]
        direction LR
        OSCAR5[("OSCAR")]
        Middleware2["Vitara<br/>Middleware"]
        VapiTTS["Vapi<br/>(TTS)"]
        Voice2["🔊 Voice Output"]

        OSCAR5 -->|"Data"| Middleware2
        Middleware2 -->|"Response"| VapiTTS
        VapiTTS -->|"Speech"| Voice2
    end

    Middleware1 --> VitaraDB2
    Middleware1 --> OSCAR4

    style Inbound fill:#dbeafe,stroke:#2563eb
    style Storage fill:#fef3c7,stroke:#d97706
    style Outbound fill:#dcfce7,stroke:#16a34a

Data Flow Decisions Review

Decision Choice Rationale Alternatives Considered
Data Ownership Split: Vitara owns config/logs, OSCAR owns PHI Clear boundaries; PIPEDA compliance; clinic retains patient data Single DB (compliance risk), Vitara caches PHI (liability), Sync copies (complexity)
PHI Access Read/Write for demographics + appointments ONLY Minimum necessary principle; no medical records ever accessed Full EMR access (overkill), Read-only (can't book), No access (can't function)
Audit Retention 7 years PIPEDA requirement; provincial health regulation alignment 1 year (non-compliant), Forever (storage cost), None (non-compliant)
Call Log Content Metadata only, no PHI Compliance; analytics without risk; patient names never logged Full transcripts (PHI risk), No logging (no analytics), Encrypted PHI (key management)

Security Boundaries

Boundary 1: Vapi → Vitara

Control Implementation
Transport HTTPS (TLS 1.2+)
Authentication Bearer Token (Vapi credential system)
Verification Timing-safe token comparison
Rate limit 100 req/min (general), 20 req/min (booking)

Boundary 2: Vitara → OSCAR

Control Implementation
Transport HTTPS (TLS 1.2+)
Authentication OAuth 1.0a (HMAC-SHA1)
Credentials Per-clinic, encrypted at rest
Scope Scheduling endpoints only

Boundary 3: Admin UI → Vitara

Control Implementation
Transport HTTPS (TLS 1.2+)
Authentication JWT tokens
Authorization Role-based (Clinic Admin, Super Admin)
Audit All actions logged

Conversation Flow: Booking

┌─────────────────────────────────────────────────────────────────────────────┐
│                      BOOKING FLOW (EXISTING PATIENT)                         │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  1. CALL CONNECTS                                                            │
│     ─────────────                                                           │
│     System: search_patient_by_phone(caller_id)                              │
│                                                                              │
│     ┌──────────────────────┐                                                │
│     │ Patient found?       │                                                │
│     └──────────┬───────────┘                                                │
│                │                                                             │
│          ┌─────┴─────┐                                                      │
│          │           │                                                      │
│         YES          NO                                                     │
│          │           │                                                      │
│          ▼           ▼                                                      │
│    "Hello [Name]"   "Thank you for calling"                                 │
│                                                                              │
│  2. IDENTIFY INTENT                                                          │
│     ────────────────                                                        │
│     Patient: "I'd like to book an appointment"                              │
│     System: Proceed to booking flow                                         │
│                                                                              │
│  3. VERIFY PATIENT (if not identified)                                       │
│     ─────────────────────────────────                                       │
│     System: search_patient(name)                                            │
│     System: "May I have your date of birth?"                                │
│     Patient: Provides DOB                                                   │
│     System: Verify match                                                    │
│                                                                              │
│  4. DETERMINE VISIT TYPE                                                     │
│     ────────────────────                                                    │
│     System: "In-person or phone consultation?"                              │
│     Patient: "In-person"                                                    │
│                                                                              │
│  5. DETERMINE REASON                                                         │
│     ────────────────                                                        │
│     System: "What would you like to see the doctor about?"                  │
│     Patient: "Prescription refill"                                          │
│     System: Map to type "B" (15 min follow-up)                              │
│                                                                              │
│  6. FIND APPOINTMENT                                                         │
│     ────────────────                                                        │
│     System: find_earliest_appointment(type=B, provider=any)                 │
│     System: "Earliest is Tuesday at 9:30 AM with Dr. Chen"                  │
│                                                                              │
│  7. CONFIRM BOOKING                                                          │
│     ───────────────                                                         │
│     System: "Shall I book that for you?"                                    │
│     Patient: "Yes"                                                          │
│     System: create_appointment(...)                                         │
│                                                                              │
│  8. CONFIRMATION                                                             │
│     ────────────                                                            │
│     System: "Confirmed for Tuesday, Jan 14 at 9:30 AM"                      │
│     System: "Please arrive 5 minutes early"                                 │
│                                                                              │
│  9. CLOSE CALL                                                               │
│     ──────────                                                              │
│     System: log_call(outcome=booked, language=en)                           │
│     System: "Thank you for calling!"                                        │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Mermaid: Booking Flow

flowchart TD
    Start(["📞 Call Connects"]) --> SearchPhone["search_patient_by_phone(caller_id)"]
    SearchPhone --> Found{Patient Found?}

    Found -->|Yes| Greet1["Hello [Name]"]
    Found -->|No| Greet2["Thank you for calling"]

    Greet1 --> Intent["Patient: 'I'd like to book'"]
    Greet2 --> Intent

    Intent --> NeedVerify{Need Verification?}
    NeedVerify -->|No| VisitType
    NeedVerify -->|Yes| SearchName["search_patient(name)"]
    SearchName --> AskDOB["Ask for DOB"]
    AskDOB --> VerifyDOB["Verify match"]
    VerifyDOB --> VisitType

    VisitType["Ask: In-person or phone?"] --> AskReason["Ask: What's the visit for?"]
    AskReason --> MapType["Map to appointment type<br/>(e.g., 'B' = 15 min follow-up)"]

    MapType --> FindSlot["find_earliest_appointment<br/>(type=B, provider=any)"]
    FindSlot --> Offer["'Earliest is Tuesday at 9:30 AM<br/>with Dr. Chen'"]

    Offer --> Confirm{Patient Confirms?}
    Confirm -->|Yes| Book["create_appointment(...)"]
    Confirm -->|No| FindAlternative["Offer alternatives"]
    FindAlternative --> Offer

    Book --> Success["'Confirmed for Tuesday, Jan 14<br/>at 9:30 AM'"]
    Success --> Reminder["'Please arrive 5 minutes early'"]
    Reminder --> LogCall["log_call(outcome=booked)"]
    LogCall --> End(["📞 'Thank you for calling!'"])

    style Start fill:#dbeafe,stroke:#2563eb
    style End fill:#dcfce7,stroke:#16a34a
    style Found fill:#fef3c7,stroke:#d97706
    style Confirm fill:#fef3c7,stroke:#d97706

Booking Flow Decisions Review

Decision Choice Rationale Alternatives Considered
Patient Identification Caller ID first, then name+DOB Fast path for known patients; secure fallback Always ask name (slow), Only caller ID (misses unlisted phones), PIN system (friction)
DOB Verification Required for name-based lookup PIPEDA identity verification; prevents PHI disclosure Security question (hard to implement), Voice biometrics (accuracy concerns), None (non-compliant)
Appointment Type Mapping Voice reason → type code Clinic-specific; LLM determines based on symptoms Explicit type selection (confusing), Fixed durations (inflexible), Staff callback (defeats automation)
Provider Selection "Any" by default, specific on request Fastest booking; respects patient preferences Always ask (slow), First available only (no choice), Require specific (delays)
Confirmation Read-back Full date/time/provider Prevents misunderstanding; audit trail Just "confirmed" (ambiguous), SMS (not implemented), None (errors)

Conversation Flow: New Patient Registration

┌─────────────────────────────────────────────────────────────────────────────┐
│                      REGISTRATION FLOW (NEW PATIENT)                         │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  1. PATIENT NOT FOUND                                                        │
│     ─────────────────                                                       │
│     System: "I couldn't find your record. Would you like to register?"      │
│     Patient: "Yes"                                                          │
│                                                                              │
│  2. CHECK REGISTRATION STATUS                                                │
│     ──────────────────────────                                              │
│     System: get_clinic_info()                                               │
│                                                                              │
│     ┌──────────────────────────┐                                            │
│     │ accepting_new_patients?  │                                            │
│     └──────────┬───────────────┘                                            │
│                │                                                             │
│          ┌─────┴─────┐                                                      │
│          │           │                                                      │
│         YES          NO                                                     │
│          │           │                                                      │
│          ▼           ▼                                                      │
│    Continue      ┌─────────────────────────┐                                │
│                  │ waitlist_enabled?       │                                │
│                  └──────────┬──────────────┘                                │
│                             │                                                │
│                       ┌─────┴─────┐                                         │
│                       │           │                                         │
│                      YES          NO                                        │
│                       │           │                                         │
│                       ▼           ▼                                         │
│                "Add to waitlist?" "Not accepting. Call back later"          │
│                                                                              │
│  3. COLLECT BC HEALTH INFORMATION                                            │
│     ──────────────────────────────                                          │
│                                                                              │
│     Required (one at a time):                                               │
│     ┌──────────────────────────────────────────────────────────────────┐    │
│     │ Field            │ Prompt                                        │    │
│     ├──────────────────┼──────────────────────────────────────────────┤    │
│     │ Full legal name  │ "What is your name as on BC Services Card?" │    │
│     │ Date of birth    │ "What is your date of birth?"               │    │
│     │ PHN              │ "What is your 10-digit PHN?"                │    │
│     │ Gender           │ "What is your gender?"                      │    │
│     │ Phone            │ "Best phone number to reach you?"           │    │
│     │ Email (optional) │ "Email address? This is optional."          │    │
│     │ Address          │ "Your mailing address?"                     │    │
│     └──────────────────┴──────────────────────────────────────────────┘    │
│                                                                              │
│  4. CONFIRM INFORMATION                                                      │
│     ───────────────────                                                     │
│     System: Read back all collected information                             │
│     Patient: "Yes, that's correct"                                          │
│                                                                              │
│  5. REGISTER IN OSCAR                                                        │
│     ─────────────────                                                       │
│     System: register_new_patient(...)                                       │
│                                                                              │
│     ┌──────────────────────┐                                                │
│     │ Registration result? │                                                │
│     └──────────┬───────────┘                                                │
│                │                                                             │
│          ┌─────┴─────┐                                                      │
│          │           │                                                      │
│       SUCCESS      ERROR                                                    │
│          │           │                                                      │
│          ▼           ▼                                                      │
│    "Welcome!"    "Let me transfer you to staff"                             │
│          │                                                                   │
│          ▼                                                                   │
│    "Book first appointment?"                                                 │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Mermaid: Registration Flow

flowchart TD
    NotFound(["Patient Not Found"]) --> AskRegister["'Would you like to register?'"]
    AskRegister --> WantsRegister{Patient says Yes?}
    WantsRegister -->|No| EndCall["End call"]

    WantsRegister -->|Yes| CheckClinic["get_clinic_info()"]
    CheckClinic --> Accepting{Accepting<br/>New Patients?}

    Accepting -->|Yes| CollectInfo
    Accepting -->|No| WaitlistCheck{Waitlist<br/>Enabled?}

    WaitlistCheck -->|Yes| AskWaitlist["'Add to waitlist?'"]
    WaitlistCheck -->|No| NotAccepting["'Not accepting.<br/>Call back later'"]
    AskWaitlist --> AddWaitlist["add_to_waitlist(...)"]

    subgraph CollectInfo["📋 Collect BC Health Information"]
        Field1["Full legal name<br/>(as on BC Services Card)"]
        Field2["Date of birth"]
        Field3["PHN (10 digits)"]
        Field4["Gender"]
        Field5["Phone number"]
        Field6["Email (optional)"]
        Field7["Mailing address"]

        Field1 --> Field2 --> Field3 --> Field4 --> Field5 --> Field6 --> Field7
    end

    CollectInfo --> ReadBack["Read back all information"]
    ReadBack --> Correct{Patient confirms<br/>correct?}
    Correct -->|No| CollectInfo

    Correct -->|Yes| Register["register_new_patient(...)"]
    Register --> Result{Registration<br/>Result?}

    Result -->|Success| Welcome["'Welcome!'"]
    Result -->|Error| Transfer["'Let me transfer you<br/>to staff'"]

    Welcome --> AskBook["'Book first appointment?'"]
    AskBook -->|Yes| BookingFlow(["→ Booking Flow"])
    AskBook -->|No| ThankYou(["'Thank you!'"])

    style NotFound fill:#fecaca,stroke:#dc2626
    style Welcome fill:#dcfce7,stroke:#16a34a
    style CollectInfo fill:#dbeafe,stroke:#2563eb
    style Accepting fill:#fef3c7,stroke:#d97706
    style Result fill:#fef3c7,stroke:#d97706

Registration Flow Decisions Review

Decision Choice Rationale Alternatives Considered
Registration Gate Check accepting_new_patients first Prevents wasted effort; clinic controls enrollment Always collect info (wastes time), No gate (clinic can't control), Manual approval (delays)
Waitlist Option Configurable per clinic Some clinics want leads; others don't Always offer (annoys some clinics), Never offer (loses leads), Clinic-wide only (inflexible)
BC Health Data PHN required for BC patients Provincial health insurance requirement; EMR integration Health card scan (no voice), Self-reported only (EMR mismatch), Skip PHN (billing issues)
Field Collection One at a time, voice-paced Reduces errors; allows corrections; natural conversation Form dump (overwhelming), All at once (error-prone), Transfer to staff (defeats purpose)
Confirmation Full read-back required Prevents data entry errors; verbal consent Skip confirmation (errors), SMS confirmation (not implemented), None (compliance risk)
Error Recovery Transfer to staff Complex cases need human; maintains trust Retry loop (frustrating), End call (poor experience), Queue callback (delay)

Guardrails

Medical Safety

Scenario System Response
Emergency keywords (chest pain, can't breathe, etc.) "Please hang up and call 911 immediately"
Medical questions "I handle scheduling only. Let me transfer you."
Medication questions Transfer to staff
Test result interpretation Transfer to staff

Conversation Quality

Scenario System Response
Tool delay > 3 seconds "Just one moment please..."
Patient not found (2 attempts) Offer registration or transfer
Frustrated caller Transfer after 2 failed attempts
Unclear intent Ask clarifying question
Off-topic request Redirect to scheduling or transfer

Data Protection

Scenario System Response
Caller asks for other patient's info "I can only discuss your own appointments"
Request for medical records Transfer to staff
PHI in logs Never logged; only metadata

Error Handling Matrix

Error Type User Message System Action
OSCAR unreachable "Let me try again..." Retry once, then transfer
Patient not found "I couldn't find your record" Offer search by phone or register
No appointments "No openings for that date" Offer alternative dates
Booking failed "I wasn't able to complete that" Transfer to staff
Registration failed "Let me transfer you to staff" Transfer with context
Invalid PHN format "PHN should be 10 digits starting with 9" Ask to repeat

Audit Trail

All calls generate audit records:

{
  "call_id": "call_abc123xyz",
  "clinic_id": "uuid-here",
  "caller_phone": "+16045551234",
  "demographic_id": 12345,
  "language": "en",
  "intent": "book",
  "outcome": "booked",
  "appointment_id": 99001,
  "duration_seconds": 127,
  "transferred": false,
  "created_at": "2026-01-12T14:32:00Z"
}

Note: No PHI is stored in call logs. Patient names, DOB, and medical details are never logged.


Next Steps