Skip to content

System Architecture

Components, Integrations, and Data Flow


High-Level Architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│                          VITARAVOX ARCHITECTURE                              │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│                                                                              │
│   ┌──────────────────────────────────────────────────────────────────────┐  │
│   │                         PATIENT LAYER                                │  │
│   │                                                                       │  │
│   │      📞 Patient calls clinic phone number                            │  │
│   │              (+1-604-XXX-XXXX via Telnyx/VoIP.ms)                    │  │
│   │                              │                                        │  │
│   └──────────────────────────────┼───────────────────────────────────────┘  │
│                                  │                                           │
│                                  ▼                                           │
│   ┌──────────────────────────────────────────────────────────────────────┐  │
│   │                         VOICE AI LAYER                               │  │
│   │                         (Vapi.ai)                                    │  │
│   │                                                                       │  │
│   │    ┌────────────┐    ┌────────────┐    ┌────────────┐               │  │
│   │    │ Deepgram   │    │  GPT-4o    │    │   Azure    │               │  │
│   │    │  Nova-2    │───▶│   LLM      │───▶│   TTS      │               │  │
│   │    │  (STT)     │    │            │    │  (Clara)   │               │  │
│   │    └────────────┘    └─────┬──────┘    └────────────┘               │  │
│   │                            │                                         │  │
│   │                     Tool Calls (Webhooks)                            │  │
│   │                            │                                         │  │
│   └────────────────────────────┼─────────────────────────────────────────┘  │
│                                │                                             │
│                                ▼                                             │
│   ┌──────────────────────────────────────────────────────────────────────┐  │
│   │                      MIDDLEWARE LAYER                                │  │
│   │                      (Vitara Platform)                               │  │
│   │                                                                       │  │
│   │    ┌────────────────────────────────────────────────────────────┐   │  │
│   │    │                    NGINX (9443)                            │   │  │
│   │    │              SSL + Rate Limiting                           │   │  │
│   │    └─────────────────────────┬──────────────────────────────────┘   │  │
│   │                              │                                       │  │
│   │    ┌─────────────────────────▼──────────────────────────────────┐   │  │
│   │    │               Voice Agent (Node.js 3001)                   │   │  │
│   │    │                                                             │   │  │
│   │    │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐        │   │  │
│   │    │  │   14 API    │  │   Clinic    │  │   OSCAR     │        │   │  │
│   │    │  │  Endpoints  │  │   Router    │  │  Adapter    │        │   │  │
│   │    │  └─────────────┘  └─────────────┘  └─────────────┘        │   │  │
│   │    │                                                             │   │  │
│   │    └────────────────────────┬───────────────────────────────────┘   │  │
│   │                             │                                        │  │
│   │              ┌──────────────┴──────────────┐                        │  │
│   │              ▼                             ▼                        │  │
│   │    ┌─────────────────┐          ┌─────────────────────┐            │  │
│   │    │   Vitara DB     │          │   OSCAR EMR         │            │  │
│   │    │  (PostgreSQL)   │          │   (Per-Clinic)      │            │  │
│   │    │                 │          │                     │            │  │
│   │    │  • Clinic cfg   │          │   • Demographics    │            │  │
│   │    │  • Hours        │          │   • Appointments    │            │  │
│   │    │  • Waitlist     │          │   • Providers       │            │  │
│   │    │  • Call logs    │          │   • Schedules       │            │  │
│   │    └─────────────────┘          └─────────────────────┘            │  │
│   │                                                                      │  │
│   └──────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Mermaid: High-Level Architecture

flowchart TB
    subgraph PatientLayer["📞 Patient Layer"]
        Patient["Patient calls clinic<br/>+1-604-XXX-XXXX"]
    end

    subgraph VoiceAI["🎙️ Voice AI Layer (Vapi.ai)"]
        STT["Deepgram Nova-2<br/>(Speech-to-Text)"]
        LLM["GPT-4o<br/>(Understanding)"]
        TTS["Azure Clara<br/>(Text-to-Speech)"]
        STT --> LLM --> TTS
    end

    subgraph Middleware["⚙️ Middleware Layer (Vitara Platform)"]
        NGINX["NGINX :9443<br/>SSL + Rate Limiting"]
        VoiceAgent["Voice Agent<br/>Node.js :3001"]
        Endpoints["14 API Endpoints"]
        Router["Clinic Router"]
        Adapter["OSCAR Adapter"]

        NGINX --> VoiceAgent
        VoiceAgent --> Endpoints
        VoiceAgent --> Router
        VoiceAgent --> Adapter
    end

    subgraph DataLayer["💾 Data Layer"]
        VitaraDB[("Vitara DB<br/>PostgreSQL")]
        OSCAR[("OSCAR EMR<br/>Per-Clinic")]
    end

    Patient --> STT
    TTS --> Patient
    LLM -->|"Tool Calls<br/>(Webhooks)"| NGINX
    VoiceAgent --> VitaraDB
    Adapter --> OSCAR

    style PatientLayer fill:#e0f2fe,stroke:#0284c7
    style VoiceAI fill:#fef3c7,stroke:#d97706
    style Middleware fill:#f0fdf4,stroke:#16a34a
    style DataLayer fill:#fce7f3,stroke:#db2777

Architecture Decisions Review

Decision Choice Rationale Alternatives Considered
Voice AI Platform Vapi.ai Turn-key voice orchestration; handles STT/LLM/TTS pipeline; 50ms latency target Build custom (too complex), Twilio Voice (no native LLM), Amazon Connect (AWS lock-in)
LLM GPT-4o Best-in-class intent understanding; function calling support; multilingual Claude (no voice integration), Llama (self-host burden), GPT-3.5 (accuracy concerns)
STT Deepgram Nova-2 Medical vocabulary support; low latency; streaming Whisper (higher latency), Google STT (cost), Azure STT (less accurate)
TTS Azure Clara Natural voice; Canadian English; SSML support ElevenLabs (cost), Amazon Polly (robotic), Google TTS (less natural)
Middleware Node.js Express Event-driven; large ecosystem; team expertise Python FastAPI (fewer libs), Go (learning curve), Java Spring (heavy)
Reverse Proxy NGINX Proven; rate limiting; SSL termination Traefik (Kubernetes focus), HAProxy (less features), Caddy (less mature)
Database PostgreSQL 16 ACID compliance; JSONB support; proven at scale MySQL (less features), MongoDB (consistency concerns), SQLite (not production)

Component Details

Telephony Layer

Component Provider Purpose
Phone Number Telnyx or VoIP.ms Inbound DID
SIP Trunk Via Vapi Call routing

Voice AI Layer (Vapi.ai)

Component Technology Purpose
Speech-to-Text Deepgram Nova-2 Transcription
LLM OpenAI GPT-4o Understanding + Response
Text-to-Speech Azure Clara Voice synthesis

Middleware Layer (Vitara Platform)

Component Technology Purpose
Reverse Proxy NGINX SSL, rate limiting
API Server Node.js + Express Webhook handling
Database PostgreSQL 16 Configuration, logs

EMR Abstraction Layer (v1.4.0)

Component Technology Purpose
EmrService services/EmrService.js Integration layer, response translation
Feature Flags config/featureFlags.js Controlled rollout (USE_EMR_ADAPTER)
Adapter Interface adapters/IEmrAdapter.js Unified EMR contract
Adapter Factory adapters/EmrAdapterFactory.js Per-clinic adapter creation
OscarUniversalAdapter adapters/OscarUniversalAdapter.js OSCAR REST Bridge client
Transformers transformers/*.js Data format normalization

Default: USE_EMR_ADAPTER=false → Legacy oscarRestService (no adapter)

EMR Layer

Component Technology Purpose
OSCAR EMR Per-clinic instance Patient data, appointments
Integration REST Bridge or OAuth 1.0a Secure API access

Network Topology

Internet
┌─────────────────┐
│   Cloudflare    │  DNS + DDoS protection
│   (DNS)         │
└────────┬────────┘
┌─────────────────┐
│  OCI Toronto    │  Production server
│  (Vitara)       │
│                 │
│  └─ NGINX:9443  │
│  └─ Node:3001   │
│  └─ Postgres    │
└────────┬────────┘
         │ OAuth 1.0a (HTTPS)
┌─────────────────┐
│  Clinic OSCAR   │  Customer EMR
│  (External)     │
└─────────────────┘

Mermaid: Network Topology

flowchart TB
    Internet["🌐 Internet"]

    subgraph CloudFlare["Cloudflare"]
        DNS["DNS + DDoS Protection"]
    end

    subgraph OCI["OCI Toronto (Vitara)"]
        NGINX2["NGINX :9443"]
        Node["Node.js :3001"]
        PG["PostgreSQL"]
    end

    subgraph ClinicNetwork["Clinic Network"]
        OSCAR2[("OSCAR EMR<br/>(External)")]
    end

    Internet --> DNS
    DNS --> NGINX2
    NGINX2 --> Node
    Node --> PG
    Node -->|"OAuth 1.0a<br/>(HTTPS)"| OSCAR2

    style CloudFlare fill:#fff7ed,stroke:#ea580c
    style OCI fill:#f0fdf4,stroke:#16a34a
    style ClinicNetwork fill:#fef2f2,stroke:#dc2626

Network Decisions Review

Decision Choice Rationale Alternatives Considered
DNS Provider Cloudflare Free tier; DDoS protection; fast propagation; analytics Route53 (AWS lock-in), Google DNS (less features), self-hosted (no DDoS)
Hosting OCI Toronto Canadian data residency (PIPEDA); free tier; low latency to BC AWS ca-central-1 (cost), Azure Canada (cost), GCP (no Canadian region at time)
SSL/TLS Let's Encrypt Free; auto-renewal; widely trusted Paid certs (cost), self-signed (trust issues), Cloudflare origin (complexity)
OSCAR Connection OAuth 1.0a over HTTPS OSCAR's native auth; proven security; per-clinic isolation Direct DB (security risk), API key (less secure), SAML (OSCAR doesn't support)

Multi-Tenancy Model

┌─────────────────────────────────────────────────────────────────────────────┐
│                        MULTI-CLINIC ARCHITECTURE                             │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│                    Vapi Phone Numbers                                        │
│                                                                              │
│    +1-604-555-0001        +1-604-555-0002        +1-604-555-0003           │
│          │                      │                      │                    │
│          │                      │                      │                    │
│          └──────────────────────┼──────────────────────┘                    │
│                                 │                                            │
│                                 ▼                                            │
│                        ┌─────────────────┐                                  │
│                        │  Vitara         │                                  │
│                        │  Middleware     │                                  │
│                        │                 │                                  │
│                        │  Phone → Clinic │                                  │
│                        │  Router         │                                  │
│                        └────────┬────────┘                                  │
│                                 │                                            │
│           ┌─────────────────────┼─────────────────────┐                     │
│           │                     │                     │                     │
│           ▼                     ▼                     ▼                     │
│   ┌───────────────┐    ┌───────────────┐    ┌───────────────┐              │
│   │  Clinic A     │    │  Clinic B     │    │  Clinic C     │              │
│   │  Config       │    │  Config       │    │  Config       │              │
│   │  (Vitara DB)  │    │  (Vitara DB)  │    │  (Vitara DB)  │              │
│   └───────┬───────┘    └───────┬───────┘    └───────┬───────┘              │
│           │                    │                    │                       │
│           ▼                    ▼                    ▼                       │
│   ┌───────────────┐    ┌───────────────┐    ┌───────────────┐              │
│   │  OSCAR A      │    │  OSCAR B      │    │  OSCAR C      │              │
│   │  (Clinic EMR) │    │  (Clinic EMR) │    │  (Clinic EMR) │              │
│   └───────────────┘    └───────────────┘    └───────────────┘              │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Mermaid: Multi-Tenancy Model

flowchart TB
    subgraph PhoneNumbers["📱 Vapi Phone Numbers"]
        Phone1["+1-604-555-0001"]
        Phone2["+1-604-555-0002"]
        Phone3["+1-604-555-0003"]
    end

    subgraph VitaraMiddleware["⚙️ Vitara Middleware"]
        Router2["Phone → Clinic<br/>Router"]
    end

    subgraph ClinicConfigs["📋 Clinic Configurations (Vitara DB)"]
        ConfigA["Clinic A Config<br/>emr_live: true"]
        ConfigB["Clinic B Config<br/>emr_live: false"]
        ConfigC["Clinic C Config<br/>emr_live: true"]
    end

    subgraph EMRSystems["🏥 EMR Systems"]
        OSCARA[("OSCAR A")]
        OSCARB[("OSCAR B<br/>(Demo Mode)")]
        OSCARC[("OSCAR C")]
    end

    Phone1 --> Router2
    Phone2 --> Router2
    Phone3 --> Router2

    Router2 --> ConfigA
    Router2 --> ConfigB
    Router2 --> ConfigC

    ConfigA --> OSCARA
    ConfigB -.->|"Demo Data"| OSCARB
    ConfigC --> OSCARC

    style PhoneNumbers fill:#dbeafe,stroke:#2563eb
    style VitaraMiddleware fill:#f0fdf4,stroke:#16a34a
    style ClinicConfigs fill:#fef3c7,stroke:#d97706
    style EMRSystems fill:#fce7f3,stroke:#db2777

Multi-Tenancy Decisions Review

Decision Choice Rationale Alternatives Considered
Tenant Isolation Phone number routing Simple; Vapi provides caller context; no auth complexity Subdomain routing (DNS complexity), API key per clinic (more complex), session-based (stateful)
Config Storage Centralized Vitara DB Single source of truth; easy backup; ACID transactions Per-clinic DB (operational burden), Redis (durability concerns), Files (no ACID)
EMR Connection Per-clinic credentials Security isolation; independent scaling; clinic ownership Shared service account (security risk), VPN per clinic (network complexity)
Demo Mode emr_live flag in DB Per-clinic control; instant toggle; no code changes Env var (all-or-nothing), Feature flag service (overkill), Code branch (risky)
Adapter Pattern Factory with caching 5-min TTL balances performance vs config freshness New adapter per request (slow), Global singleton (no isolation), IoC container (heavy)

Request Flow

Inbound Call

1. Patient dials +1-604-555-0001
2. Telnyx routes to Vapi
3. Vapi answers, starts assistant
4. Vapi calls search_patient_by_phone webhook
5. Vitara looks up clinic by phone number
6. Vitara queries clinic's OSCAR for patient
7. Response returned to Vapi
8. Vapi speaks response to patient

Booking Flow

1. Patient: "I'd like to book an appointment"
2. Vapi: Understands intent, calls find_earliest_appointment
3. Vitara: Queries OSCAR for available slots
4. Vitara: Returns earliest slot
5. Vapi: "The earliest is Tuesday at 9:30 AM"
6. Patient: "Yes, book that"
7. Vapi: Calls create_appointment
8. Vitara: Creates appointment in OSCAR
9. Vitara: Logs call in Vitara DB
10. Vapi: Confirms to patient

Security Architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│                        SECURITY BOUNDARIES                                   │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  BOUNDARY 1: Internet → NGINX                                               │
│  ─────────────────────────────                                              │
│  • TLS 1.2/1.3 (Let's Encrypt)                                             │
│  • Rate limiting (100 req/min)                                              │
│  • HSTS, security headers                                                   │
│                                                                              │
│  BOUNDARY 2: Vapi → Vitara                                                  │
│  ────────────────────────────                                               │
│  • Bearer Token (Authorization header)                                      │
│  • Timing-safe token comparison                                             │
│  • Vapi credential system                                                   │
│                                                                              │
│  BOUNDARY 3: Vitara → OSCAR                                                 │
│  ─────────────────────────────                                              │
│  • OAuth 1.0a (HMAC-SHA1)                                                   │
│  • Per-clinic credentials                                                   │
│  • Encrypted at rest (AES-256)                                              │
│                                                                              │
│  BOUNDARY 4: Admin UI → Vitara                                              │
│  ─────────────────────────────                                              │
│  • JWT authentication                                                        │
│  • Role-based access (RBAC)                                                 │
│  • Audit logging                                                            │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Mermaid: Security Architecture

flowchart LR
    subgraph Internet["🌐 Internet"]
        Caller["Caller"]
        Admin["Admin User"]
    end

    subgraph Boundary1["🔒 Boundary 1: TLS"]
        NGINX3["NGINX<br/>TLS 1.2/1.3<br/>Rate Limiting<br/>HSTS"]
    end

    subgraph Boundary2["🔑 Boundary 2: API Auth"]
        VapiAuth["Vapi Webhook<br/>Bearer Token"]
    end

    subgraph Boundary3["🔐 Boundary 3: Admin Auth"]
        JWTAuth["JWT Auth<br/>RBAC Middleware<br/>Audit Logging"]
    end

    subgraph Boundary4["🏥 Boundary 4: EMR"]
        OAuth["OAuth 1.0a<br/>HMAC-SHA1<br/>Per-Clinic Creds"]
    end

    subgraph Core["⚙️ Voice Agent"]
        VoiceAgent2["Node.js<br/>Express"]
    end

    subgraph External["🏥 Clinic"]
        OSCAR3[("OSCAR EMR")]
    end

    Caller -->|"HTTPS"| NGINX3
    Admin -->|"HTTPS"| NGINX3
    NGINX3 -->|"Vapi calls"| VapiAuth
    NGINX3 -->|"Admin UI"| JWTAuth
    VapiAuth --> VoiceAgent2
    JWTAuth --> VoiceAgent2
    VoiceAgent2 --> OAuth
    OAuth -->|"HTTPS"| OSCAR3

    style Boundary1 fill:#fef2f2,stroke:#dc2626
    style Boundary2 fill:#fff7ed,stroke:#ea580c
    style Boundary3 fill:#fef3c7,stroke:#d97706
    style Boundary4 fill:#f0fdf4,stroke:#16a34a

Security Decisions Review

Decision Choice Rationale Alternatives Considered
TLS Termination NGINX with Let's Encrypt Industry standard; free certs; easy renewal App-level TLS (performance hit), Cloudflare SSL (proxy mode issues)
Vapi Authentication Bearer Token Vapi's credential system; timing-safe comparison; simple integration API Key header (deprecated), mTLS (overkill), IP allowlist (fragile), HMAC (Vapi doesn't send signatures)
Admin Authentication PassportJS + JWT Stateless; scalable; proven library Sessions (stateful), OAuth2 (external dependency), Basic Auth (insecure)
Password Storage bcrypt (cost 12) Adaptive hashing; GPU-resistant Argon2 (newer but less support), PBKDF2 (faster cracking), SHA256 (too fast)
Account Lockout 5 attempts / 15 min Balances security vs usability; industry standard Progressive delays (complex), CAPTCHA (accessibility), No lockout (brute force risk)
EMR Auth OAuth 1.0a OSCAR's native protocol; signature-based OAuth 2.0 (OSCAR doesn't support), API keys (OSCAR doesn't support)
Credential Storage Encrypted at rest (AES-256) PIPEDA compliance; defense in depth Plain text (non-compliant), Vault (complexity), KMS (cloud lock-in)
Audit Logging Append-only table Immutable; compliance-ready; 7-year retention File logs (harder to query), External service (latency), None (non-compliant)

Next Steps