A voice hotline that turns "something's wrong and I don't know who to call" into a specialist appointment request — inside one phone call.
Built in 24 hours at the YC × Medplum Agentic Healthcare Hackathon (Y Combinator office, San Francisco).
Patients are routinely handed homework they cannot do. "See a specialist" means: figure out which specialty, find one nearby who takes your insurance, confirm that's actually true, and get on their calendar.
The system loses people at every step:
| ~35% | of specialist referral scheduling attempts end in a completed appointment (Duke, 103k referrals, JGIM 2018) |
| 52% | of Medicare Advantage directory listings had at least one inaccuracy (CMS audit, 2018) |
| 33% | of in-network phone numbers were dead or wrong; only 18% of calls reached a bookable appointment (Senate "ghost networks" secret-shopper study, 2023) |
| ~27% | of Gen Z adults can define "deductible" (NAIC, 2024) |
Existing voice AI answers one clinic's phone — which assumes you already know where to call. OneCall is the call that starts one step earlier.
A caller describes a symptom out loud. Within a single conversation the agent:
- Screens for emergencies with a deterministic red-flag check — before any model reasoning runs
- Selects a specialty from a retrieved referral-criteria corpus, and reads the matching criteria back to the caller
- Runs a real 270/271 benefits transaction and reports precisely what it did and did not verify
- Finds real nearby clinics from the CMS NPPES registry, then enriches them with public-web research in the background
- Writes an appointment request to Medplum as FHIR R4 — and calls it proposed, never booked
These are the parts I'd want to talk through in an interview.
checkRedFlags() (lib/triage/redflags.ts) is pure deterministic regex over the transcript — nine patterns covering MI, stroke, respiratory, hemorrhage, and suicidal-ideation presentations. It runs before every other tool and re-runs whenever the caller adds symptoms.
The reasoning: the one judgment where a hallucination is catastrophic is the one judgment that should not be probabilistic. Patterns are also written to catch how people actually talk — MI-referred pain is matched on "pain going down my arm" without requiring the word "chest", because callers frequently never say it.
A triggered red flag still produces a clinical record: the route writes a FHIR Encounter with an emergency disposition. Even the abort path leaves an audit trail.
This is the piece I'm most careful about. The Stedi test key accepts only published synthetic fixtures, so the transaction is real 270/271 wire traffic against a synthetic Aetna policy — not the caller's actual coverage.
Rather than paper over that, the response object carries the distinction explicitly:
{
network: "unknown", // a 270/271 does not establish provider participation
callerCoverageVerified: false,
requestedPayerUsedInTransaction: false,
providerParticipation: {
status: "unknown-not-verified",
reason: "A standard 270/271 does not reliably establish provider participation.",
},
}The voice agent is prompted to say "Stedi's test transaction ran successfully. Its synthetic Aetna fixture returned a sample office-visit benefit; it did not check your insurance policy or this doctor's network" — and is explicitly forbidden from saying "your copay" or "you're covered."
A demo that lies about insurance coverage is worse than no demo. The 271 parsing is genuinely real; the scope of the claim is what's constrained.
Provider identity comes from live CMS NPPES lookups — real NPIs, real addresses, Haversine-filtered against ZIP centroids within the caller's stated radius. Every provider card links back to its NPPES record.
Appointment availability, however, is not public data for anyone. So the API returns availability: { status: "not-public" } and the agent says so, rather than inventing slots that look convincing on stage.
Appointments are written as FHIR status: "proposed", not "booked", with the clinic participant marked needs-action. The agent's prompt bans the words booked, scheduled, confirmed, and reserved.
Writes are idempotent via createResourceIfNoneExist() keyed on a deterministic identifier, and Medplum errors are deliberately allowed to propagate — lib/fhir/transaction.ts documents this as intentional, so the API can never report a booking that didn't persist.
LLMs hallucinate SNOMED, LOINC, and ICD-10 codes fluently and invisibly. Every clinical concept here uses a display-only CodeableConcept ({ text: "..." }) instead of a fabricated code. An unfilled code is an honest gap; a wrong one is a silent data-integrity bug that a clinical reviewer would catch immediately.
Deep provider research can take tens of seconds. Instead of leaving dead air, research_providers returns immediately with NPPES identity data plus a publicWebResearch: { status: "running" } marker; enrichment continues in the background and streams in when ready. A 45-second cap degrades to registry-only results.
Conversation state is tracked in a oneCallWorkflow object attached to every tool result (lib/voice/workflow.ts), which pins the agent to the next unfinished step. This is what stops the classic voice-agent failure of re-asking for a ZIP code it was already given.
Browser (WebSocket audio)
│
▼
Deepgram Voice Agent ──── nova-3 STT · aura-2 TTS · function calling
│
├─ check_red_flags ──────── deterministic regex ──┐
├─ determine_specialty ──── Moss retrieval ───────┤
│ (local fallback) │
├─ check_eligibility ────── Stedi 270/271 ────────┤
├─ research_providers ───── CMS NPPES + web ──────┤
└─ request_appointment ────────────────────────── ▼
Medplum
(FHIR R4)
| Layer | Choice | Notes |
|---|---|---|
| Voice | Deepgram Voice Agent API | Single WebSocket for STT, LLM turn-taking, and TTS |
| Retrieval | Moss | Indexes the referral corpus; falls back to a local token-scored retriever if unavailable, so triage degrades instead of failing |
| Eligibility | Stedi | Synchronous JSON 270/271, with typed error classes per failure mode |
| Records | Medplum | FHIR R4 system of record — chosen because it's built for compliant PHI |
| Directory | CMS NPPES | Public provider registry; no API key required |
| App | Next.js 16 (App Router), TypeScript, Tailwind | Deployed on Vercel |
Notable resilience detail: every external dependency has a defined degradation path. Moss down → local retrieval. Stedi unconfigured → typed 503, flow continues. Telephony unconfigured → "booking request sent." Web research times out → registry-only results. Nothing in the demo path is a single point of failure.
app/
call/ Voice call UI — live transcript, evidence panel, provider comparison
api/ triage · eligibility · providers · appointment-request · confirm-call
lib/
triage/ Deterministic red flags + grounded specialty selection
eligibility/ Stedi client, typed errors, 271 → summary mapping
directory/ NPPES search, distance filtering, background web enrichment
fhir/ Typed R4 resource builders and transactional writes
voice/ Deepgram agent config, prompt, and workflow state machine
data/ Synthetic referral-criteria corpus
docs/internal/ Hackathon working notes: research, decisions, run-of-show
Requires Node 24 (see .nvmrc).
npm install
cp .env.example .env.local # fill in credentials
npm run dev # http://localhost:3000
npm run check # typecheck · lint · buildUnit tests are Node's built-in runner, no framework:
node --test lib/**/*.test.mjsDeepgram and Medplum credentials are required for the full call flow. Without Moss, Stedi, or telephony keys the app still runs on its documented fallback paths.
Stated plainly, because a demo that hides its edges isn't worth showing.
- Appointment availability is not real. No public API exposes clinic slots; that's a business-development problem, not an algorithmic one.
- Eligibility runs against a synthetic fixture. Stedi's production endpoint is the same API — the 271 parsing is already real; the test key is what constrains it.
- No identity or insurance-card verification. Card OCR is commodity tech that wasn't the interesting part.
- Triage routes; it does not diagnose. Specialty selection comes from retrieved referral criteria. The only severity judgment in the system is the deterministic 911 screen. A real deployment would be a navigation tool under clinical oversight, not a medical device.
- Single-session state.
lib/eligibility/pending.tsholds one demo session in process memory; concurrent callers need session keying. - HIPAA posture is incomplete. Medplum is the system of record precisely because it's built for compliant PHI, but BAAs with the voice and retrieval vendors would be required before real patient data touched this.
Built by Jayden and Tanay at the YC × Medplum Agentic Healthcare Hackathon, August 2026.