Inspiration
International students arrive in the US with student health insurance and no map of how US medical billing works. A clinic visit produces two documents, a bill and an insurer's Explanation of Benefits (EOB), written in unfamiliar jargon and often in a second language. When the bill asks for more than the EOB says you owe, most people just pay it.
What it does
SecondLook takes a photo or PDF of your bill and EOB (or a CSV, or a built-in demo), and:
Reconciles each line: flags any line where the bill asks for more than your EOB says you owe, to the cent. Checks prices: places each charge among other providers' charges for the same CPT code, using public CMS Medicare data, and reports a percentile with a 90% confidence interval. Explains the findings in your language (10 languages, including Chinese, Korean, Hindi and Spanish). Drafts an appeal email in English to the billing office, citing every disputed line.
How we built it
Python + Streamlit for a fast, phone-friendly UI. Pure-Python core (reconcile.py, pricing.py): Decimal money math, a $0.50 rounding tolerance, and explicit can't verify results for messy input. Statistics: mid-rank empirical percentiles with a bootstrap 90% CI. Because the percentile only depends on counts below and equal to the charge, we sample those counts from a multinomial distribution. This is the exact bootstrap distribution, at O(B) cost instead of O(B·n). A price is only called clearly high when the percentile is ≥ 95 and the CI's lower bound is ≥ 90. Real data: 78,968 provider-level charges for 16 common CPT codes from data.cms.gov. Claude API (claude-opus-5-5): structured-output transcription of bill photos, plus explanations and emails. Claude never does arithmetic. Every $ amount and % in its output is checked against the computed facts. A mismatch triggers a regeneration with feedback, and repeated failure falls back to a deterministic template. 89 pytest tests, all runnable offline with a fake API client.
Challenges we ran into
Keeping an LLM honest about money. Early drafts called a $180 amount due "the amount billed" and rounded a 99.9th percentile to "100%". We fixed this with unambiguous fact names, precise percent formatting, and a verifier that rejects any number not in the facts. Real data at hackathon speed. data.cms.gov rate-limited us mid-download; we made the fetcher resumable with backoff and cached the result in the repo so the demo never depends on the network. Bootstrap cost. A naive bootstrap over tens of thousands of providers is slow; the multinomial reformulation made it instant.
Accomplishments that we're proud of
An end-to-end flow from photo to verified appeal email in under a minute. Every number a user sees is computed by code and traceable to a test. The app degrades gracefully: no API key, network failure or a bad transcription never breaks it.
What we learned
Let the LLM do what it is good at (reading and writing), and keep numbers in code. Uncertainty matters: a percentile without a confidence interval overstates what small samples can tell you.
What's next for Secondlook
Empirical-Bayes shrinkage so codes with few reference charges borrow strength from similar codes. Negotiated-rate data from hospital price-transparency files, closer to what student plans pay. Partnering with university international student offices and student health insurance plans.
Log in or sign up for Devpost to join the conversation.