Personal project / Voice AI · Case Study

Voice AI Patient-Intake Agent

A phone agent that registers new patients over a U.S. number, in English or Spanish, and saves nothing until the caller confirms a read-back.

RolePersonal project · built end to end
TimelineOct 2026
DomainPatient intake · Voice AI
ShapeLLM agent, REST API, dashboard
Quality53 passing tests
HostingRailway + Vercel
Patient registry dashboard listing patients registered by phone
The Brief

Registration by phone, with no half-saved records.

Callers give their details to a voice agent instead of a form. The hard parts are accuracy and safety: names spelled right, invalid dates caught, and no duplicate or half-written patient when a call drops or the network stalls.

Names spelled and confirmed letter by letter; numbers read back once as they are given
Dates, phone numbers, states and ZIP codes validated twice: Zod in the API, CHECK constraints in Postgres
Returning callers found by phone number and offered an update instead of a duplicate
Mock appointment booking from open slots; a slot can only be booked once
English and Spanish; transcripts and summaries stored per call and linked to the patient
Architecture & Stack

One validation contract for the agent, the API and the database.

Caller
U.S. phone numberPSTN
Voice
Retell AITelephony · speech-to-text
LLM agent14-section prompt · 5 tools
ElevenLabsText-to-speech
API
Signed webhooksHMAC check on the raw body
Fastify + ZodTool handlers · REST routes
Data
PostgreSQLDrizzle · patients, calls, appointments
Dashboard
Next.jsServer components, no keys in the browser
Stack
Voice
Retell AIOpenAI modelElevenLabs
Backend
Fastify 5Zod 4Drizzle ORM
Data
PostgreSQL
Frontend
Next.js 16Tailwind 4
Quality
VitestPGlite
Hosting
RailwayVercelDocker
Key Decisions
01The model changes state only through five tools, and every tool validates with the same Zod schemas as the REST API
02Nothing is written before a confirmed read-back, and write tools never retry, so one confirmation cannot create two records
03Every tool call and call event is signature-checked over the raw body before any handler runs
04Prompt, tool schemas and agent settings live in the repo and are provisioned by an idempotent sync script
05Integration tests run the real migrations on PGlite and pin the model's tool schemas to the API's
Walkthrough

Inside the product.

Screenshots from a local run on fictional demo data.

Patient registry · Next.js dashboard
Patient record with identity, contact, address and insurance
One patient record, as the agent saved it
Call transcript showing name spelling, read-back and booking
Call summary and transcript, linked to the record
Spanish-language call transcript with an emergency contact
Caller switches to Spanish mid-call
Call where the caller corrects a phone digit and types an ID on the keypad
A corrected digit and keypad entry for an ID
Agent & API
register_patient tool definition with max_retry 0
The register tool: no retries, confirmed fields only
System prompt section on confirmation before saving
Prompt rules for read-back and saving
OpenAPI documentation listing patient endpoints and signed webhooks
OpenAPI docs generated from the Zod schemas
5Tool-calling functions
53Passing tests
14Prompt sections

A voice agent that can only touch data through validated tools: invalid input comes back as field-level errors the agent re-asks, and a dropped call leaves nothing behind.

Next project

Crypto-Derivatives Trading System

→