An internal web app that gave a care team without reliable EMR access a longitudinal view of patient health — built on claims data.

Karoo Health manages cardiovascular care for patients attributed through contracts with health plans, including Zing Health and Humana. The care team's ability to do their job well depends on having an accurate, up-to-date picture of each patient's health status before every appointment.
On the Zing Health side, Karoo had no direct relationship with providers — a patient's health history was effectively a black box. On the Humana side, a direct relationship existed, but EMR access was inconsistent and often unreliable, with real connectivity issues just getting into the systems at all.
Either way, the care team was left without visibility into patient health status — forced to rely on patient recall during limited appointment time, for an older cardiovascular population that often couldn't accurately remember their own medications, procedures, or diagnoses. Time spent re-collecting information patients had already given a doctor somewhere else was time not spent on the actual care plan.
Scoped the project end to end and worked with the Chief Medical Officer and clinical/ops staff to define what data mattered and how it should be prioritized.
Built the Python pipeline that ingested, cleaned, de-duplicated, and cached monthly claims data at scale.
Designed and built the web app itself — the information architecture, the layout, and the interaction model — and iterated on it directly with daily users.
I worked closely with Karoo's Chief Medical Officer and clinical/ops staff to figure out what actually mattered: which cardiology CPT codes, which diagnosis codes, which medications were worth surfacing versus noise. That input shaped both what the pipeline filtered for and how information should be organized — fast access to what's actionable now, without losing the full historical record that tells a patient's longer health story.
The users were Karoo's own clinical staff, and the fastest path to a useful tool was working directly with the people who'd set clinical priorities and the people who'd use it every day.
Solving this properly meant building an internal platform with real database access — but neither the engineering team nor the data team had the bandwidth to take that on. So I built the product myself, on infrastructure Karoo already had: a Google Apps Script web app for the interface, with a Sheets-backed pipeline underneath it as the datastore. Fast to build, and nothing new to provision.
The one obvious concern with that approach — storing real patient health data outside a purpose-built system — was already handled. Karoo's Google Workspace is HIPAA-compliant, so gating the app behind Workspace login delivered real access control for free. There was no login system to build and no auth to secure; that risk was covered by infrastructure Karoo already trusted.
The care team never opens the spreadsheet. They go to a URL and get a purpose-built web app — my own layout, type hierarchy, and interaction model, written in code. Sheets is invisible infrastructure behind it, holding rows. It isn't the product, and nobody using the tool ever sees it.
Owning the front end meant owning the information hierarchy — what gets grouped, what stays collapsed, what a user sees first — none of which a spreadsheet would have let me control. And because the UI was decoupled from the data underneath it, the interface could be redesigned week to week without touching the pipeline.
The app presents five sections in a fixed order: Patient → PCP → Medications → Diagnoses → Procedures. That sequence is the core design decision of the whole tool. Identification comes first, because acting on the wrong patient is the one unrecoverable error. Provider contact comes second, because it's the escape hatch when something doesn't add up. Then the clinical record runs in descending order of how changeable it is during a call: medications can be adjusted today, diagnoses provide context, procedures are history. A care team member reading top-to-bottom moves from "who is this" to "what can I do about it" without ever backtracking.
A daily-refreshed cache keeps lookups fast: the first of the day takes about eight seconds while the cache warms, and every one after lands in one to two seconds. Eight seconds is long enough that an unexplained blank screen reads as a broken tool — so the cold start needed a deliberate loading state, and the warm path needed none at all.
Each month, new claims files arrive from the health plans as raw CSVs — not structured for this purpose, and large: over 10 million raw line items across sources. A Python pipeline ingests, cleans, and de-duplicates that down to roughly 200,000 records that actually matter: medications reduced to the latest fill, diagnoses and procedures filtered to cardiology relevance. That preprocessing isn't a nice-to-have — raw volume at this scale exceeds what Sheets can hold directly, so the heavy lifting has to happen before anything touches the datastore.
The full monthly rerun — from raw files to a refreshed dataset — takes 5–10 minutes. Everything downstream of that is built to be instant, because the team needed to search a patient and get an answer without waiting around mid-appointment-prep.
Name, ID number, date of birth, and contact info — enough to positively identify the patient before anything else.
Provider name, health group, direct phone, fax, email, and address — a fast path to contact the provider if questions come up.
Full history, de-duplicated down to the latest fill, with a computed doses-per-day to support adherence conversations.
Cardiology-relevant history, grouped under umbrella conditions, tied to the diagnosing provider.
Cardiovascular procedure history, tied to the performing provider via automated NPI lookup.
The tool's biggest effect on the call itself was turning the conversation from recall into verification. Asking a patient to remember their own medications, procedures, and diagnoses cold — especially an older patient, on a rushed call — is unreliable by nature. Asking them to confirm what's already on file, or fill in a specific gap, takes far less of them, and holds up on every call regardless of how sharp their memory is that day.
How unreliable recall actually is turned out to be the number that justified the whole project:
In practice, that reshapes the whole appointment-prep workflow:
The clearest proof that the information architecture worked came from the days it wasn't supposed to. When appointments ran back-to-back and there was no prep time at all, care team members pulled the app up mid-call and read from it live — unrehearsed, with a patient already on the line — and the calls still went well.
One click moves lookup data directly into Karoo's internal EMR, removing the manual re-entry that would otherwise eat into appointment-prep time.
A shareable, exportable format serves referral partners like Heartbeat Health and Empassion, who face the same information gap Karoo's own team did — removing the guesswork that was originally part of every handoff.
The tool kept evolving through direct, daily feedback from the people using it. The clearest example: procedures were originally shown by name alone, but the team still couldn't act on that — a procedure without knowing who performed it isn't actionable. Adding the performing provider meant augmenting the pipeline to map procedures to NPI numbers, which in turn justified building an automated NPI lookup that scrapes and caches provider info, so future lookups are instant and the corpus of known providers keeps compounding.
Smaller changes mattered too. Medications, diagnoses, and procedures were reordered newest-first, since a five-year-old diagnosis matters less than one from last month. Medication entries gained a computed doses-per-day, calculated from day supply and units prescribed — directly supporting adherence assessment for a population where missing a dose schedule has real cardiovascular consequences. And diagnoses, often cluttered with near-duplicate codes as conditions like hypertension or diabetes progress, got grouped under umbrella categories, with individual codes still available on expand.
"The lookup tool makes it sooooo much easier for me, without it I would be going into patient calls blind. Thank you!!!"
The two health plans failed the care team in different ways, and the gap between them is the clearest measure of what the tool changed. On Humana, where EMR access existed but was flaky, prep ran ten to fifteen minutes per patient at minimum — when it worked at all. On Zing, prep wasn't slow. It was impossible. There was no source to prep from; the team went in blind, every time.
With the lookup tool, prep on both sides collapsed to under five minutes, from the same starting point: a patient ID.
Built for a small internal audience, the tool reached well beyond the care team it was scoped for. Past the core users — patient engagement specialists, RNs, social workers, and dietitians — it saw regular use from the director of clinical and market operations leads. One market ops lead used it to get insight into a patient who was a high utilizer of the ER, with no other path to understanding their condition: a case where the tool filled a gap for someone entirely outside its original audience.
Claims arrive roughly 30 to 60 days behind the care they describe. The tool shows a longitudinal history, not a current state — a medication started last week will not appear in it. That's why the framing with the care team was always verification, not truth: the app tells you what to ask about, and the patient confirms or corrects it. Treated as a source of truth it would be actively dangerous; treated as a prompt, the lag barely matters.
A claim records what was billed, not what was prescribed, taken, or clinically intended. Diagnosis codes carry billing incentives that don't always map to a clinician's actual assessment. This is part of why the CMO's input on which codes to surface mattered so much — filtering was as much about excluding noise as including signal.
The pipeline exists in part because Sheets can't hold the raw volume. That works at ~17K patients and three data sources; it doesn't extend to significantly more plans or new data types without the real platform underneath it.
A cardiologist-inference feature is in progress — piecing together a patient's likely current cardiologist from procedure history on the Zing side, where no formal provider relationship exists. Beyond care-team visibility, this builds a proprietary map of provider relationships across Karoo's patient population, useful for future partnership and business-development opportunities.
Try the exact experience a Karoo care team member gets — a live, interactive replica of the tool, running on dummy data instead of real patient records.
Try the live demo