%%{init: { "theme": "base", "themeVariables": { "background": "#0f0f0f", "primaryColor": "#161616", "primaryTextColor": "#e0e0e0", "primaryBorderColor": "#3a3a3a", "lineColor": "#555555", "secondaryColor": "#111111", "tertiaryColor": "#0f0f0f", "clusterBkg": "#0d0d0d", "clusterBorder": "#2a2a2a", "titleColor": "#888888", "edgeLabelBackground": "#0f0f0f", "fontFamily": "DM Mono, monospace", "fontSize": "13px", "nodeBorder": "#3a3a3a", "mainBkg": "#141414" } }}%% graph TB subgraph EXT[" ☎ EXTERNAL"] PAT["☎ Patient\nPhone call"] VAPI["◈ Vapi Platform\nVoice AI · Webhooks"] end subgraph APP[" ⚡ APPLICATION LAYER"] CADDY["⟳ Caddy\n:80 · :443 TLS Proxy"] FAPI["⚡ FastAPI\n:8000 Webhook · API"] GEM["✦ Google Gemini\nLLM intent summary"] end subgraph DATA[" ▣ DATA LAYER"] PG[("▣ PostgreSQL\n:5432\nCalls · Events\nIdempotency")] RDS[("⟳ Redis\n:6379\nJob queue")] end subgraph WORK[" ⚙ WORKER"] ARQ["⚙ arq Worker\nRetry · Callbacks\nOutbound call"] end subgraph OBS[" ◉ OBSERVABILITY"] REXP["redis-exporter\n:9121"] PROM["Prometheus\n:9090"] GRAF["Grafana\n:3000"] MON["Monitor UI\n/monitor"] end subgraph INFRA[" ⬆ CI/CD & INFRASTRUCTURE"] GHA["GitHub Actions\nTest → Build → Deploy"] GHCR["GHCR\nDocker Registry"] TF["Terraform\nStaging VPS"] R2["R2\nCloudflare R2 backups"] end %% ── Core request path ────────────────────────────────────── PAT -->|"voice stream"| VAPI VAPI -->|"HTTPS webhook\ntool-calls · events"| CADDY CADDY -->|"HTTP :8000"| FAPI FAPI -->|"asyncpg\nSQL write"| PG FAPI -->|"enqueue\nfailed job"| RDS FAPI <-->|"summarise\nintent"| GEM %% ── Worker path ───────────────────────────────────────────── RDS -->|"poll queue"| ARQ ARQ -.->|"poll /healthz\nevery 5s"| FAPI ARQ -.->|"outbound call\non recovery"| VAPI %% ── Observability scraping ────────────────────────────────── REXP -->|"scrape\nredis metrics"| RDS PROM -->|"scrape /metrics"| FAPI PROM -->|"scrape :9121"| REXP GRAF -->|"PromQL queries"| PROM MON -.->|"GET /api/monitor/stats\nevery 5s"| FAPI %% ── Infra flows ───────────────────────────────────────────── PG -.->|"pg_dump\nscheduled"| R2 GHA -->|"push image"| GHCR GHA -->|"SSH deploy\ndocker compose pull"| FAPI GHA -->|"terraform apply"| TF %% ── Styles ────────────────────────────────────────────────── classDef core fill:#141414,stroke:#4a4a4a,stroke-width:1.5px,color:#f0f0f0 classDef data fill:#111,stroke:#333,stroke-width:1px,color:#d0d0d0 classDef worker fill:#111,stroke:#2e2e2e,stroke-width:1px,color:#c0c0c0 classDef obs fill:#0e0e0e,stroke:#252525,stroke-width:1px,color:#aaa classDef infra fill:#0d0d0d,stroke:#222,stroke-width:1px,color:#888 classDef ext fill:#111,stroke:#3a3a3a,stroke-width:1.5px,color:#e0e0e0 class FAPI,CADDY core class PAT,VAPI ext class GEM worker class PG,RDS data class ARQ worker class REXP,PROM,GRAF,MON obs class GHA,GHCR,TF,S3 infra
Synchronous / primary flow
Async / polling / scheduled
Core application
Data / Worker
Observability
CI/CD & Infrastructure

Component Reference

FastAPI core
Ingests Vapi webhook events (call-start, tool-calls, transcript, call-end). Validates schemas via Pydantic, writes idempotent call records to Postgres using a unique constraint on (call_id, event_type), invokes Gemini for summaries, and returns tool results synchronously.
Vapi Platform external
Cloud voice AI service. Manages phone number +14787584217 and AI assistant "Sarah". Posts all call lifecycle events to the webhook URL over HTTPS and provides an outbound calling API for the arq worker callback path.
Caddy proxy
Reverse proxy with automatic TLS via Let's Encrypt. Terminates HTTPS on :443 and forwards to FastAPI on :8000. In local dev mode, proxies to host.docker.internal:8000 so the server can run outside Docker.
PostgreSQL data
Stores Call and CallEvent records. Unique constraint on (call_id, event_type) ensures webhook retries are idempotent — duplicate events are detected and silently dropped without returning 5xx to Vapi.
Redis + arq Worker queue
When the sync handler fails (DB down or Gemini error), a job is enqueued to Redis. The arq worker polls /healthz every 5 seconds; on recovery it places an outbound Vapi call back to the patient's number using failed_interaction_callback.
Google Gemini AI llm
Called from the tool-calls handler to summarise patient intent (name, DOB, reason) into a concise clinical note before storing in Postgres. Falls back gracefully to a template string if the Gemini API is unavailable.
Prometheus observability
Scrapes /metrics from FastAPI every 5s (HTTP request counts, latency histograms p50/p95/p99, 5xx rate via prometheus-fastapi-instrumentator) and redis-exporter (arq queue depth). Fires alerts on thresholds defined in alert.rules.yml.
Grafana observability
Visualises Prometheus data. Pre-provisioned datasource and dashboards under grafana/provisioning/. Shows request rate, latency percentiles, error rate, and Redis queue depth panels.
Monitor UI internal
Custom HTML dashboard at /monitor served by FastAPI itself. Polls /api/monitor/stats every 5s for live call counts, event log with per-event latency, service health indicators, and Grafana credentials.
GitHub Actions ci/cd
On push to main: (1) run Pytest in ephemeral docker-compose, (2) build Docker image and push to GHCR tagged with Git SHA, (3) auto-deploy to Staging via SSH, (4) deploy to Production behind a manual approval gate.
Terraform infrastructure
Provisions the Staging VPS on Contabo and manages DNS via Namecheap. Configuration in /infra. Production was manually provisioned and is managed deploy-only through GitHub Actions SSH.
Cloudflare R2 reliability
scripts/backup.sh runs pg_dump, gzip-compresses, and pushes to a Cloudflare R2 bucket using the AWS CLI with --endpoint-url https://<ACCOUNT_ID>.r2.cloudflarestorage.com. R2 is fully S3-compatible — no region needed (auto). scripts/restore.sh supports restore from local path or s3:// R2 URI.

CI/CD Pipeline

⬆
Push to
main
→
🧪
Pytest
ephemeral DB
→
🐳
Docker build
tag: git SHA
→
📦
Push to
GHCR
→
🚀
Deploy
Staging
auto
→
✅
Deploy
Production
manual gate