"Your health, handled."
An autonomous medical concierge that cross-references patient records against live conversations and webcam scans to prevent dangerous drug interactions — and places real pharmacy calls on the patient's behalf.
Managing prescriptions, refill timing, and allergy safety is overwhelming — especially for patients juggling multiple medications or relying on caregivers. A single missed interaction or an unnoticed allergen can have serious consequences.
Onyx was built to act as a always-on medical proxy: ingest a patient's medical history once, then use that dossier to power every interaction — voice, vision, and telephony — so safety checks and pharmacy logistics happen automatically, not after the fact.
Onyx is a full-stack medical concierge with a luxury dashboard UI and a FastAPI backend wired to real AI and telephony services.
Onboarding — Upload a medical PDF or image. Gemini 2.0 Flash (via OpenRouter) parses it into a structured patient JSON profile: demographics, allergies, current medications, caregiver contacts, and refill metadata.
Dashboard — A glassmorphism patient command center shows the dossier, medication list, refill status, scan history, and a notifications bell. Patients speak to Onyx via the browser mic; responses are synthesized with ElevenLabs eleven_v3.
Visual Pill Scanner — Hold a medication bottle up to the webcam. GPT-4o-mini Vision identifies the drug, class, and a plain-language description, then cross-checks it against the patient's known allergies. A CRITICAL alert fires before the patient can consume a conflicting drug.
Real-Time Telephony — When a refill or prescription action is needed, Onyx places an outbound Twilio call to the pharmacy. The message is synthesized with ElevenLabs eleven_turbo_v2_5 (low-latency telephony mode), played via TwiML <Play>, and the pharmacist can confirm with speech or DTMF. If they decline, Onyx responds with a persuasion follow-up.
Refill Reminders — A daily APScheduler job flags medications whose last_fill_date + days_supply is within reminder_days_before, surfaces a banner in the dashboard, drives the notifications bell, and can trigger an automated pharmacy call.
flowchart LR
subgraph Browser["Browser (Patient UI)"]
onboarding["onboarding.html"]
dashboard["index.html (dashboard)"]
webcam["Webcam pill scan"]
mic["Mic → /api/chat → TTS"]
bell["Notifications bell"]
end
subgraph Backend["FastAPI Backend (main.py)"]
dossier["patient_profile.json"]
process["/api/process_report"]
scan["/api/scan"]
tts["/api/tts"]
alert["/api/caregiver/alert"]
twilio_conv["/api/twilio/conversation"]
scheduler["APScheduler"]
end
subgraph Services["External Services"]
openrouter["OpenRouter\n(Gemini + GPT-4o-mini)"]
elevenlabs["ElevenLabs"]
twilio["Twilio Voice"]
pharmacy["Pharmacy phone line\n(CAREGIVER_PHONE_NUMBER)"]
end
Browser <-->|REST / static| Backend
process --> openrouter
scan --> openrouter
tts --> elevenlabs
alert --> elevenlabs
alert --> twilio
twilio_conv --> elevenlabs
twilio <-->|TwiML Gather / Play| pharmacy
All LLM calls route through OpenRouter — a single OPENROUTER_API_KEY covers both Gemini (document parsing) and GPT-4o-mini (chat + vision).
flowchart TD
A[Upload medical report] --> B[Gemini extracts allergies + medications]
B --> C[Save to patient_profile.json]
C --> D[Scan pill bottle via webcam]
D --> E[GPT-4o-mini Vision with allergy context]
E --> F{allergy_conflict?}
F -->|true| G[CRITICAL ALLERGEN DETECTED alert]
F -->|false| H[Safe — show drug info]
G --> I[Persist to scan_history]
H --> I
I --> J[Surface in notifications bell for 7 days]
flowchart TD
A[Patient requests refill via voice or UI] --> B["/api/chat sets alert_triggered: true"]
B --> C["/api/caregiver/alert with intent + meds"]
C --> D[ElevenLabs synthesizes pharmacy message]
D --> E[Audio → CDN or /api/audio via BASE_URL]
E --> F[Twilio outbound call with TwiML Play + Gather]
F --> G{Pharmacist response}
G -->|yes / press 1| H[Confirm refill and hang up]
G -->|no| I["/api/pharmacy/response persuasion follow-up"]
| Layer | Tech | Purpose |
|---|---|---|
| Frontend | HTML, Tailwind CSS, GSAP | Onboarding flow + patient dashboard (vanilla JS, no framework) |
| Backend | Python, FastAPI, APScheduler | REST API, static file serving, daily refill scheduler |
| Document parsing | Gemini 2.0 Flash via OpenRouter | PDF/image → structured patient JSON |
| Chat + Vision | GPT-4o-mini via OpenRouter | Intent detection, conversational replies, pill identification |
| In-app TTS | ElevenLabs eleven_v3 |
Natural voice responses in the browser |
| Telephony TTS | ElevenLabs eleven_turbo_v2_5 |
Low-latency audio for Twilio <Play> |
| Telephony | Twilio Voice + TwiML | Outbound pharmacy calls, inbound conversation handler |
| PDF parsing | PyMuPDF (fitz) |
Extract text from uploaded medical PDFs |
| Tunneling | localhost.run SSH / localtunnel |
Expose local server so Twilio can reach webhooks |
Onyx/
├── README.md
├── .env.example # API key template
├── patient_profile.json # Generated at runtime (patient dossier)
├── image.png # Dashboard screenshot
│
├── frontend/
│ ├── onboarding.html # Medical document upload + profile initialization
│ └── index.html # Main dashboard (scanner, chat, notifications, refills)
│
└── backend/
├── main.py # FastAPI app — all API routes + static serving
├── requirements.txt # Python dependencies
├── start_tunnel.py # localhost.run SSH tunnel → writes BASE_URL to .env
├── start_lt.py # localtunnel fallback
├── get_models.py # ElevenLabs voice discovery utility
├── sim_pharmacy.sh # curl helper to simulate pharmacist DTMF response
└── audio_cache/ # Generated at runtime (TTS MP3 cache for Twilio)
| Endpoint | Method | Description |
|---|---|---|
/onboarding |
GET | Serve onboarding page |
/api/patient |
GET | Return full patient dossier |
/api/process_report |
POST | Upload PDF/image → Gemini extraction → save profile |
| Endpoint | Method | Description |
|---|---|---|
/api/chat |
POST | STT transcript → intent + alert_triggered + spoken reply text |
/api/tts |
POST | Text → ElevenLabs MP3 audio stream |
/api/convaI/message |
POST | Multi-turn conversation handler (call SID + speech) |
| Endpoint | Method | Description |
|---|---|---|
/api/scan |
POST | Base64 webcam image → drug ID + allergy conflict check |
/api/scan/history |
GET | Scan history (?bookmarked_only=true optional) |
/api/scan/history/{id} |
PATCH | Toggle bookmark on a scan entry |
| Endpoint | Method | Description |
|---|---|---|
/api/refill/status |
GET | Medications due for refill (banner data) |
/api/refill/request-pharmacy |
POST | Trigger pharmacy call for due medications |
/api/notifications |
GET | Unified bell feed (refill due, upcoming, allergy alerts) |
| Endpoint | Method | Description |
|---|---|---|
/api/twilio/conversation |
POST | TwiML webhook — inbound call conversation loop |
/api/caregiver/alert |
POST | Outbound pharmacy call with synthesized message |
/api/pharmacy/response |
POST | TwiML webhook — handle pharmacist yes/no response |
/api/audio/{audio_id} |
GET | Serve cached TTS audio for Twilio <Play> |
- Python 3.9+
- Node.js +
npx(only if using thelocaltunnelfallback instart_lt.py) - API keys for Twilio, ElevenLabs, and OpenRouter
git clone https://github.com/Akhileshreddym/Onyx.git
cd Onyxpip install -r backend/requirements.txtCopy .env.example to .env at the project root and fill in your keys:
OPENROUTER_API_KEY=...
ELEVENLABS_API_KEY=...
TWILIO_ACCOUNT_SID=...
TWILIO_AUTH_TOKEN=...
TWILIO_PHONE_NUMBER=...
CAREGIVER_PHONE_NUMBER=... # pharmacy number Onyx will callBASE_URL is written automatically by the tunnel script in step 5.
From the project root:
uvicorn backend.main:app --host 0.0.0.0 --port 8000 --reloadIn a second terminal:
python3 backend/start_tunnel.pyThis forwards port 8000 through localhost.run and writes a public *.lhr.life URL into .env as BASE_URL. A localtunnel alternative is available at backend/start_lt.py.
Configure your Twilio phone number's voice webhook to
${BASE_URL}/api/twilio/conversation(POST) so incoming calls hit Onyx.
Initialize a patient profile first:
http://localhost:8000/onboarding
Then open the main dashboard:
http://localhost:8000/
- Deep forest-black backgrounds (
#050705) with subtle radial gradients - Glassmorphism panels — frosted blur, gold wire borders, soft drop shadows
- Serif display type (Playfair Display / Cormorant Garamond) paired with Inter for body text
- Classic gold accents (
#D4AF37) for CTAs, badges, and active states - Critical alerts in deep red (
#ef4444) with pulsing border animations
| Aspect | Status | Notes |
|---|---|---|
| PDF parsing → patient profile | Correct | Gemini 2.0 Flash via OpenRouter extracts structured JSON from uploaded documents |
| Webcam pill scanner + allergy check | Correct | GPT-4o-mini Vision identifies drug and cross-references patient allergies |
| Twilio outbound pharmacy calls | Correct | TwiML <Play> + <Gather> with ElevenLabs synthesized audio |
| Persuasion follow-up on pharmacist decline | Correct | /api/pharmacy/response webhook handles no/decline responses |
| Browser speech-to-text | Limitation | Uses browser Web Speech API (Chrome-focused); no dedicated STT provider; silent fallback if mic captures nothing |
| Patient storage | Limitation | Single patient_profile.json file, not a real database or EHR |
| HIPAA compliance | Limitation | Not compliant — no encrypted storage, audit logs, or BAA-covered providers |
| Tunnel dependency | Limitation | Requires localhost.run or localtunnel for Twilio webhooks, not production-ready |
- Dedicated STT provider (Deepgram / Whisper) instead of browser Web Speech API
- FHIR / EHR integration instead of PDF upload
- SMS caregiver alerts via Twilio Messaging
- Multi-patient household support
- HIPAA-compliant deployment (encrypted storage, audit logs, BAA-covered providers)
- Proactive drug interaction checks across the full medication list (not just allergies)
| Name | GitHub |
|---|---|
| Akhilesh Reddy Mallu | @Akhileshreddym |
| Shawn Madadha | @ShawnMadadha |
