It's 4:47 PM. There are nine prior authorizations in the queue and the person working them has been at it since lunch. Each one is the same ritual: open the chart, hunt for the diagnosis codes, cross-reference the payer's coverage policy PDF (the one that changed last quarter and nobody told you), copy the ICD-10 that justifies the CPT, paste it into a portal that logs you out every twenty minutes, and hit submit knowing there's maybe a 70% chance it comes back denied for a reason you could have caught if you'd had time to read all eleven pages of the policy.
Then it comes back denied. And now it's an appeal, which is the whole thing again but angrier.
If you've built anything near a medical practice, you know this queue never empties. It just ages. And every aged auth is revenue sitting in limbo. I spent the last year building software to work this pile, and the interesting part wasn't the AI — it was realizing prior auth isn't a writing problem. It's a matching problem.
The pile is a matching problem, not a writing problem
The naive build is: dump the chart into an LLM, say "write a medical necessity letter," ship it. It produces confident, fluent prose. It also gets denied, because a payer doesn't grade prose. A payer grades whether specific, enumerated criteria in their coverage policy are each backed by something in the chart.
So the real unit of work isn't a letter. It's a set of (criterion → evidence) pairs. Aetna's policy for a given procedure might require: failed conservative therapy for ≥6 weeks, documented functional impairment, imaging confirming the indication. UnitedHealthcare wants a different three things. The job is to bind each criterion to a concrete chart citation — and to notice, before submitting, when a criterion has no evidence behind it. That empty binding is your denial risk, and you can see it coming.
def assemble_justification(chart, payer_policy):
bindings = []
for criterion in payer_policy.required_criteria:
evidence = retrieve_evidence(chart, criterion) # dated chart citations
bindings.append({
"criterion": criterion,
"evidence": evidence,
"satisfied": bool(evidence),
})
unmet = [b for b in bindings if not b["satisfied"]]
denial_risk = len(unmet) / len(bindings) # the number you want BEFORE submit
return Justification(bindings=bindings, risk=denial_risk, gaps=unmet)
The gaps list is the whole product, honestly. It turns a blind submission into "these two criteria are unmet — go get the PT notes before you send this."
Grounding, because hallucinated evidence is malpractice
retrieve_evidence is where every shortcut bites you. You cannot let the model say the conservative therapy happened. It has to cite the note that says it, with a date. We treat any binding whose evidence isn't traceable to a real chart span as unsatisfied — a false "satisfied" is far more dangerous than a false gap. A gap gets flagged for a human; a fabricated citation gets you denied, or worse.
The other subtle bit: coverage policies drift. The criteria set has to be re-parsed from the live policy, not memorized, or you'll assemble a beautiful justification against last year's rules.
The denial is structured too
When a denial comes back, resist the urge to "write an appeal." A denial cites specific reasons — usually a criterion the payer claims wasn't met. So the appeal is just the inverse operation: take each denial reason, find the criterion it maps to, and rebut it with the evidence binding you already built. Point by point, in the payer's own vocabulary. Half the time the evidence was there and the reviewer missed it, and a targeted rebuttal is far stronger than a fresh persuasive essay.
What actually changed
The lesson that generalizes beyond healthcare: when an LLM task feels like "generate a document," check whether it's secretly a matching task with an audit trail. Model the criteria, bind them to evidence, expose the gaps, and let the fluent-prose part be the last, cheapest step. The pile still exists — but now something works it down instead of just aging it.
We packaged this as Authorize — try it free. It's how we built the thing that finally empties the queue.
Top comments (1)
Prior auth is a good fit for policy matching because the hard part is not generating text, it is preserving the evidence path from patient facts to payer rule to requested action.