# Overturn: the denials small clinics stopped fighting

> Source: <https://dev.to/abhilegend/overturn-the-denials-small-clinics-stopped-fighting-5ano>
> Published: 2026-10-18 14:38:46+00:00

*This is a submission for the [Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle](https://dev.to/challenges/hacktoberfest-week2-2026-10-12)*

Overturn is an appeal agent for small clinics. You give it a denial notice, and it tells you what the denial means, how likely an appeal is to win, what to do next, and it drafts the letter. It runs on the clinic's own machine.

Every small clinic has a drawer of denied claims it has quietly given up on. Each one is real money, often a few thousand dollars, and each takes a billing person about twenty minutes to read, decode, check, and write up. At a few hundred denials a month, most of them get written off, because the work costs more than the return.

Overturn changes that math. Open denials are ranked by expected recovery, not claim size, so the billing person starts with the ones worth the time. For each one, the app tells them whether to appeal, correct and resubmit, or write the claim off.

**Who pays for it:** the clinic, or the billing service that works denials for several clinics. Each denial the app makes worth working is recovered revenue.

[PLACEHOLDER: a 3 to 5 minute video, or a deployed link. The Render blueprint is written but I have not deployed it, so the demo is a local recording. Follow the shot list in `overturn_demo_flow.html`.]

[PLACEHOLDER: GitHub repository link, embedded here]

The repo has the Python app, the playbook of the twelve most common denial codes, the test suite, the Mastra agent, the Tinker training pipeline, and deployment files.

The whole pipeline runs on open components.

**The model.** A local open-weight model served by Ollama. The current run uses `qwen2.5:7b-instruct`. The model writes only the argument of the appeal. Everything else in the letter is built from the notice's facts.

**The guardrail.** A fact-checker compares every code, CPT, and amount in the draft against the source notice. A failed draft gets one retry. If it fails again, the app falls back to a template letter and shows the rejected draft.

**The odds.** A scikit-learn model trained on claim history, with TabPFN wired in as the preferred option once an API key is set. The app also reports the levers that would move the odds, such as attaching clinical notes.

**Retrieval.** Past wins are searched with BM25 keyword matching and vector search, merged with reciprocal rank fusion, and stored in Postgres with pgvector on Tiger Cloud.

**Durable workflow.** Each step checkpoints. A crashed run resumes by run ID, and failed steps retry with backoff. The same steps run on Temporal.

**Fine-tuning.** About 300 supervised pairs built from the playbook and past wins, filtered through the fact-checker, then a LoRA adapter trained on Tinker. The tuned model is compared with the base model in the arena.

**Comparison.** A Model arena runs the same synthetic notices through a local baseline and hosted open-weight models on Backboard, ranked by how often the first draft passes the fact-checker.

**Agent.** A Mastra agent, Denial Desk, with two tools that call Overturn's API. It answers "what should I work on first?" by calling the queue tool, and it analyzes a specific claim by calling the analysis tool. I ran both in Mastra Studio.

Privacy is the first reason. A denial notice carries a patient name, a member ID, and a diagnosis. With a local open model there is no business associate agreement to negotiate with an API vendor and nothing to leak. The claim is only as strong as the data-location check, which is why I map every path below.

Cost is the second. Per-token pricing on every notice, retry, and redraft eats the margin on a claim worth $90. A local model is a fixed cost, so every denial is worth working. [PLACEHOLDER: tokens and latency per draft, measured from the trace]

Control is the third. Open weights let the clinic swap models with one environment variable, fine-tune on its own winning appeals, and compare models on its own data. A closed API would make each of those a vendor conversation.

The honest caveat: the Model arena and the Tinker sampler are cloud paths. The arena only accepts synthetic notices, and the app warns before it runs. The header's external-call counter tracks model calls, so it does not yet cover every outbound path. I'm fixing that before I claim it.

[OPTIONAL PLACEHOLDER: DevRelay session link or embed. Remove this section if you don't save one.]

The idea came from my friend Manoj Pareek, who works in this business. I gave him the app, and he used it. He was very happy with it, and as a thank-you he bought me a KFC treat. It was my first earning from Overturn.

It was a gift, not a payment for a license, so I am not counting it as revenue. What it shows is that a person in the business found the app useful enough to say so. Next I want to see whether he or another billing team would pay for it, and at what price.

I am entering these partner categories, because the project uses each technology in a real code path:

**Tiger Data** (Partner): pgvector retrieval on Tiger Cloud.

**Mastra** (Partner): the Denial Desk agent, which calls the Overturn API through two tools.

I left out Render, TabPFN, and Entire. Render is written but not deployed. TabPFN is configured but falls back to scikit-learn in my current run. Entire has a guide, but I haven't enabled it on a real session.

The data is synthetic. The odds model is only as good as a clinic's history, and a new clinic has little. Drafts are drafts, not legal advice. Appeal windows and criteria vary by payer, so the notice has the final word.

The Mastra agent now runs. In one test, I asked it about the queue, and it called the queue tool and returned the top claim. I then asked it to analyze CLM-26009, and it called the analysis tool, which returned an overturn probability of 0.612, an expected value of $3,765.26, and appeal as the best move. The agent's answer matched the app's own dashboard. Several pieces are still not run end to end: the Temporal kill-and-resume demo and the Render deployment. The Tinker comparison numbers and the Backboard arena results are still to be measured. I have marked each one rather than let the demo suggest more was tested than was.

Thanks for reading, and thanks to the Hacktoberfest and DEV teams for the challenge.
