Back to projects
Carelyze · Dental insurance workflow · 2025–2026

Every dollar
shows its work.

Carelyze prices a dental visit from the carrier’s own portal before the patient sits down. I’m its founding design engineer — carelyze.com, the CareClinic workspace, and its patient links — and every decision below is shown with the artifact that forced it.

Founding design engineerProduct design · UX · React/TypeScript

Two-person buildThe Today redesigns and the confidence removal are mine; the rules engine is my co-founder’s lead.

In productionBay Area dental practices · Delta Dental + MetLife live

Product designReact / TypeScriptDesign systemsWorkflow & AI boundaries
Carelyze
Every number keeps its source
carelyze.com
carelyze.com hero: Fewer surprise bills. Less under-collection. No more portal marathons.
The public pitchLive
CareClinic · pre-visit patient cost estimate
CareClinic estimate with an expanded rule trace: how this number was calculated, rules and inputs applied
The rule traceDemo capture
carelyze.com · proves it
CareClinic · quotes it
Patient links · can't confirm it
My commits · careClinic
168 of 239 · May–Aug 2026
Provenance
8 source kinds · one badge per number
Deterministic core
3,368 lines · zero LLM calls
The design contract
Tomorrow priced before it starts
The design problem

The number is the easy part. Defending it at the front desk is the product.

What a patient owes depends on the treatment plan, the clinic’s contracted fees, and benefits that live behind a carrier portal login with MFA. Front desks reconcile it by hand every morning — or trust the EHR’s stale snapshot and absorb the write-off. The design docs answer with owners of truth: the portal owns benefits, Dentrix owns the calendar, and ambiguity always has somewhere to go.

The accuracy doctrine, verbatim: “The carrier portal is the ground truth. Our job is to interpret it correctly and completely, not to rebuild it.”

A dental practice's treatment-case printout: four procedures on tooth 14, EHR-estimated patient portion $580.50
Real document · my own visitA practice’s EHR treatment-case printout, used as the product’s test case.
Where every estimate starts

The input arrives with someone else’s guess already on it.

Four procedures on tooth 14, a fee column, and the EHR’s own estimate: patient portion $580.50 — priced from whatever benefits were last keyed in. Behind it sits a six-form paper intake packet and a portal session someone has to babysit.

Carelyze reads this document, pulls live coverage, prices every line — and then puts its result next to the EHR’s number, match or differ-by-$X. The design docs call that figure “a hint, not ground truth.”

Four sources, one deterministic engine, a trace on every line

source_kind · a closed taxonomy
treatment_plan
Treatment plan

Parsed from the uploaded PDF — an LLM extracts codes and fees, never invents them.

portal
Carrier portal

Pulled live, per-code and per-patient, from the carrier's own provider portal.

fee_schedule
Fee schedule

The clinic's contracted fees — a real uploaded row, or one learned from claims.

observed_charge
Observed charge

A prior clinic billed amount — usable input, never a contract rate.

estimator/engine.py · 1,842 of 3,368 lines
“Same inputs → same output, byte-for-byte. No I/O. No LLM.”
compute_allowed_amountapply_deductiblecheck_waiting_periodcheck_frequency_limitapply_downgradecheck_missing_tooth_clause
Every line carries
  • Provenance — which source supplied each number
  • rules_evaluated — the human-readable rule trace
  • Flags — info · warn · error, never silent
  • Clinician adjustments — appended and audited

The decision: provenance is an enum, not a caption. Eight source_kind values — the four sources above plus portal_category, estimator, no_data, and assumed — cover every number the estimator can emit, so the UI renders trust mechanically, and a mock or assumed value cannot be styled like a live one.

01Trust model

The confidence score looked reassuring. I deleted it.

The May estimator summarized trust as a percentage, with a threshold routing low scores to review. But a front desk about to quote a number doesn't ask how sure the software feels — it asks which number came from where. That reframe is the decision I'd defend hardest: on July 15 I removed the score outright, and a closed provenance taxonomy took its job.

CareClinic · May 2026
“High confidence (99%)” — one green number vouching for the whole estimate
Before · demo capture, mock coverage“High confidence (99%)” — one green number vouching for the whole estimate
CareClinic · estimate detail
The estimator narrates itself — a crown, expanded: allowed = min(billed $935, contracted $835), the deductible's turn, and a 7-step rule trace
After · demo capture, shipped modelThe estimator narrates itself — a crown, expanded: allowed = min(billed $935, contracted $835), the deductible's turn, and a 7-step rule trace
The early model · spec’d May 2026 · since removed
Estimate confidence
< 0.85 overall → needs_human_review
99%

One number answered for the whole estimate — and answered none of the front desk’s real questions: which fee, which benefit, which rule needs attention before quoting.

The shipped model · one badge per number
backed by a source
Live · Delta DentalFee scheduleTreatment planPlan defaultComputed
loud by design
Amount unavailableAssumedMock data

The frontend’s provenance.ts calls itself “the single source of truth for where did this number come from?”. Amber tones are counted and surfaced loudly — a mock pull degrades every derived badge, so a mock benefit can never masquerade as a confirmed one.

af42335 · 2026-07-15 · my commit“Remove estimate confidence and simplify history status”confidence.py deleted · −101 lines

“Never guess; flag ambiguity for staff.”

Written for patient matching — wrong patient means wrong PHI and wrong estimate — and enforced everywhere after: ambiguous fee schedules become a staff pick, unmatched read-backs become review items, and a June commit is titled “careClinic: eliminate all coverage-percent assumptions and fallbacks.”

Missing data is a state, not a guess

fee_schedule_status · pricing_status
matched

priced against the complete carrier + plan contract

accumulating

verified per-code fees; catalog known incomplete

observed_charge

prior clinic charges — provisional, not contract rates

ambiguous

several plan schedules fit — staff must pick

no_schedule

no fee schedule uploaded for this plan

no_carrier

carrier not in the catalog

pricing_status: unpriced

“…the portal may affirm coverage, but dollar amounts must not be quoted until a fee is supplied.” A missing number renders as an em-dash, never a plausible-looking $0.00 — in a money interface, that difference is a refund conversation.

The decision: six named states instead of one empty cell. Every “we don’t know” has a different fix — upload a schedule, pick a plan, wait for a claim — so each ships as its own status carrying its own next action. A blank field would have made all six look like the same shrug.

CareClinic · estimate detail
A clinician edit marks the row “edited,” recolors the totals banner, and offers “Reset all overrides” — the calculation underneath is never destroyed
Demo capture · Jul 2026A clinician edit marks the row “edited,” recolors the totals banner, and offers “Reset all overrides” — the calculation underneath is never destroyed
1

Corrections sit beside the math, never over it

A clinician adjustment lives in its own table; “the estimator output remains untouched” and every change lands in an append-only audit log. A database constraint keeps edited rows honest: billed = insurance + patient, always.

2

Deleted, with a guard test

The removal wasn’t a revert waiting to happen. A validator strips the retired score from old stored estimates, and the estimate screen’s test suite now asserts the word “confidence” never renders. Deleting your own feature is a decision; testing for its absence is follow-through.

3

The EHR is a signal, not a source

The practice’s own estimate appears as a delta badge — match or differ-by-$X — never as an input to the calculation. Agreement is a trust cue; disagreement is the product working.

AI with a narrow job

Two LLM call sites. Zero in the calculator.

The entire backend calls a model in exactly two modules — both turn a PDF into typed structure through a Pydantic tool schema. The extraction prompt is versioned in code, and one of its rules reads the way the whole product thinks: “If a field is missing or unreadable, use null. Do NOT guess.”

The tool boundary is the trust boundary — everything after it is deterministic.

treatment_plan_parser.py

Claude Sonnet · reads the FEE column only — EHR-side estimates are explicitly ignored

fee_schedule_parser.py

Claude Opus · one full-PDF pass into a typed fee table

estimator/ · 3,368 lines

Pure rules. No I/O, no LLM import — behind 654 backend tests

02Scheduling

Write the intent. Believe only the read-back.

Estimates trust the carrier portal; scheduling trusts the practice management system — “Dentrix is the committed-schedule system of record; Carelyze is the working surface.” So a successful write job is not a confirmed appointment: the appointment is confirmed when Dentrix says it back, and the state machine makes any other claim unrepresentable. The whole bridge is invariant-tested against a faithful Dentrix mock — the real database comes at the on-site visit.

Who is allowed to say “confirmed”

appointment_transitions.py · actor-gated
requested

A patient can ask or cancel — never confirm.

staff
confirming

Write queued to Dentrix through the bridge; the UI shows the wait honestly.

dentrix_readback
confirmed

Only the read-back actor may complete this edge. Staff can’t. The system can’t.

sync_stateDentrix: queuedDentrix: awaiting read-backDentrix: ✓ written
manual_pending · enter in Dentrixconflict · intent kept beside the source rowfailed · surfaced for staff

every externally visible state gets a badge — no silent states

A job result is never a confirmation

When the bridge reports a successful write, the row moves only to awaiting_readback — “a write ack happens ONLY here (read-back), never from a job result POST.”

Ambiguity becomes a review item, not a merge

Read-back rows link to pending intent only on exactly one deterministic match; anything ambiguous becomes a Dentrix-origin row and a review item for staff.

Patients request everything, confirm nothing

Public booking and bearer links — token hashes only, an OTP step-up for intake — cover cancel, reschedule_request, intake, feedback. The page comment: “There is deliberately NO confirm action here.”

The decision: every surface gets its verbs — staff queue writes, patients request and cancel, and only the Dentrix read-back may say “confirmed.”

03Daily workflow

The front door moved twice. The version that stuck does less.

Upload-first, then patient-first, then day-first. The end state is a screen that intentionally does less: Today scans the imported schedule, triages coverage, and stops at patient selection — “the shortest path from an imported clinic schedule to an estimate,” in its own docstring. Both redesign commits are mine.

May 2026 · Upload-first

Process one PDF

Drop a treatment plan, get an estimate. Useful — and detached from the clinic's day.

Jul 7 · Patient-first

Start from a person

“Step 1 is the patient, not the carrier” — coverage on file comes along for free.

Jul 14 · Day-first

Prepare who's next

“The shortest path from an imported clinic schedule to an estimate.” Today is the front door.

Recreated for this page · real labels, synthetic patients
The screen that does less on purpose
Scan → resolve → estimate, in one pass
clinic.carelyze.io / today
Today
Monday, August 24

Import today’s appointments, then choose a patient to start their estimate.

TodayTomorrow
Import today’s appointments
Today’s patients
Appointments imported from NexHealth or Dentrix · estimates support Delta Dental and MetLife
Insurance checks
1 verified · 1 needs attention · 2 still queued
9:00a
confirmed
Ava Nguyen Recommended now
Dr. Feng · Op 2
Ready to estimateCoverage verified
Start estimate
10:30a
requested
Leo Patel
Dr. Feng · Op 1
Insurance neededCoverage check queued
Add insurance
1:15p
confirmed
Maya Romero
Dr. Feng · Op 2
Needs member ID + date of birthCoverage needs review
Add details
2:45p
confirmed
Sam Ortiz
Dr. Feng · Op 3
Unsupported carrierCoverage check queued
View details
One recommendation, never two
The in-progress or next appointment gets the only blue badge; an unsupported carrier never displaces a patient Carelyze can finish.
Pending stays grey
“A coloured chip there would compete with the ‘Recommended now’ cue” — the queue's own comment. Attention is spent where staff can act.
Reasons ride along
The carrier's explanation is fetched only when something on the day is short of verified — and lands in the queue as a sentence.
04The design system

The marketing site animates. The money screen refuses to.

One brand serves a persuasion room and a clinical room. Two token files share a wordmark and a typeface, and one rule outranks both palettes: color only ever means state, and green only ever means verified.

Two rooms, one wordmark

globals.css @theme · both repos
CareClinic — the clinical room
brand-900
#2c2060
royal purple · the anchor
brand-600
#6536ad
primary action fill
brand-500
#7e4fc6
meters · selection ring
brand-100
#e9e0f9
active-step ground
accent-500
#df7c4c
coral · deductible meter
accent-300
#f2b48f
warm accent text

“a royal purple drawn from the logo wordmark, with a warm coral accent from the logo wing” — the token file’s own header

carelyze.com — the persuasion room
brand-800
#442089
logo wordmark
brand-600
#6a3fc4
CTA fill
lilac
#d998eb
logo petal
ember
#f9875c
logo petal · warnings
pulse
#0e7e96
“cyan = live data”
success
#047857
“insurance pays” dollars

Different violets on purpose — the site sells in the logo’s brighter #442089, the workspace works in a calmer #2c2060. The rule they share is stricter than a hex: color only ever means state.

A face per job

next/font · both repos
Estimated patient responsibility
Quicksand · the heading face of both rooms — one global h1–h5 rule in the workspace
$580.50 · D2740
JetBrains Mono — the site’s token file says it plainly: “dollars + CDT codes”
Import today’s appointments, then choose a patient to start their estimate.
Body: Roboto in the workspace · IBM Plex Sans on the site

The components, running

shipped classes, live
Coverage verifiedRecommended now
cards rounded-2xlbadges rounded-fullmoney tabular-nums
clinic.carelyze.io / login
The login panel states the thesis over trust chips a clinic can verify: live from the portal, MFA handled, no EHR integration required
Live · clinic.carelyze.io/loginThe login panel states the thesis over trust chips a clinic can verify: live from the portal, MFA handled, no EHR integration required

Voice is a safety feature

Error: portal request failed (502)
Nothing is wrong with Carelyze or your data — the insurance carrier's portal is temporarily unreachable.
portal outage renders as a warn, not an error — blame is routed accurately
Estimate could not be generated
No estimate was produced. Numbers based on synthetic coverage are never shown — fix the underlying issue and try again.
the failure state restates the safety rule instead of apologizing
Status: needs_review
MetLife verification needs the member ID on the insurance enrollment — add it and press Verify now again
every needs_review lands in the queue with its raw reason, written as a sentence

Every right-column line is shipped product copy. Each pair encodes a policy: route blame accurately, restate the safety rule instead of apologizing, deliver the raw reason as a sentence a human can act on.

Recreated for this page · running specimens

The craft is a motion budget

0 animation libraries in the workspace
Building the estimate…
  1. Reading the treatment plan
  2. Checking insurance coverage
  3. 3Calculating the estimate
polls every 1000 ms · labels are “plain language, not insurance jargon”
Annual maximum
$955.50 left after
Used before
$0.00
Applied by estimate
+$544.50
Remaining after
$955.50
three segments, one track · the full sentence lives in the aria-label
The persuasion room moves

carelyze.com animates hard: a GSAP revenue-cycle ring with every node placed by ring(deg), SVG steps that draw their own strokes, an activity log at 620 ms per line — all gated behind prefers-reduced-motion.

The clinical room holds still

CareClinic ships no motion library. Outside the scripted import demo, its one timed animation is the login carousel — 6 s per slide, deliberately not pausing on hover because “that made it look frozen while reading.” Everything else is a state change.

Live · Delta DentalFee scheduleTreatment planAssumed

Carelyze’s signature element is ten pixels tall: the source pill that follows every number, with its exact origin one hover away.

The persuasion room, over time

Four hero headlines to find one bad morning.

I designed and built carelyze.com through all four eras — 66 of its 69 commits. Each pivot narrowed the audience and sharpened the verb, and the last rewrite came out of sales calls, not a brainstorm.

Four hero headlines, quoted from git

care-landing · 69 commits · 2025–2026
Jun 2025Broad healthcare AI

“Supercharge Healthcare Quality with AI Analyst”

CAHPS/HEDIS analytics for clinics. Many capabilities, no urgent buyer.

Feb 2026Preventive outreach

“Automate Annual Physicals & Boost Revenue”

SMS/email outreach funnels for small clinics. Closer, still a nice-to-have.

May 2026Dental estimator

“Know what every patient owes — before they’re in the chair”

One document, one number, one desk — the first version a front desk could run on a real morning.

Jul 2026The AI insurance team

“Fewer surprise bills. Less under-collection. No more portal marathons.”

The estimator becomes the first hire of a team; claims and denials become the roadmap.

The decision: stop describing capability, start naming a bad morning. The July hero isn’t a brainstorm output — the commit is titled “Homepage revision from sales feedback: pain-first hero, demo video, 6 carriers”.

The delivery boundary

What’s live, what’s built to contract, what’s a demo.

A case study about evidence should survive its own standard. Everything above is sourced from the product, its git history, and its design docs — and the boundary between shipped and aspirational is drawn below, the same way the product draws it.

Live in production
Delta Dental + MetLife verification on the carriers' own portals — Playwright login, clinic-managed MFA
Deterministic estimator with per-line rule trace and provenance
NexHealth schedule + insurance sync (real API)
Today triage · estimate history · clinician adjustments
Patient booking, intake and action links (OTP step-up)
Built to contract, awaiting on-site
Dentrix bridge: transport, cursors, leases, read-back matcher — invariant-tested against a faithful mock
All three Dentrix write backends are explicit NotImplementedError stubs until the on-site probe
The write→read-back hash promise has a named test; it has never met a real Dentrix database
Demo & direction, labeled
carelyze.com/try — the public walkthrough, scripted end to end
Claims · denial analysis · appeals — “In development” on the public platform map
The demo lane, expanded

A demo that can’t lie — it isn’t wired to anything.

carelyze.com/try had to feel like the product on first pass while being structurally incapable of touching PHI or a live carrier session. So it’s scripted end to end — three sample plans, a staged activity log — and the interface discloses that itself, on every screen. One tell survives: the demo still wears the coarse confidence chip the product itself deleted in July.

The demo tells on itself

carelyze.com/try

Fully scripted — “no PHI, no carrier calls, no backend” is the first comment in the demo's source

Labeled in the UI itself: “This is a mockup”, “no file is uploaded and no carrier is contacted”

Three sample plans stress different failure surfaces: deductible unmet, already met, near the annual max

The decision: disclosure in the interface’s voice, not in fine print. A visitor who skips every paragraph still cannot mistake it for a live pull.

carelyze.com / try
The result teaches the product's grammar: per-line coverage, benefit meters, rule notes, and a “▼ $212.00 lower” delta vs. the EHR
Live · carelyze.com/try · scripted demoThe result teaches the product's grammar: per-line coverage, benefit meters, rule notes, and a “▼ $212.00 lower” delta vs. the EHR
The honest gaps

The Dentrix write path still ends at three NotImplementedError stubs — it has never met a real Dentrix database. The /today Dentrix import dialog — my own screen — plays a scripted timeline and, unlike every other demo surface in the product, doesn’t label itself as one in its UI; that label is the first fix I’d ship. And “tomorrow priced before it starts” is still a design contract, not a measured median — no clinic-week has been instrumented yet.

Reflection

Provenance is more honest than a score — and much harder to demo.

Deleting the confidence number cost the pitch its one-frame proof: eight badge states take thirty seconds to explain, and the score took none. I’d make the same call again — and next time design the thirty-second version of provenance first. Every enum name, hex, and quote in this page’s diagrams is copied from the product; where a screen is recreated rather than captured, its label says so.