HCC gap capture in under 60 lines of Python
A working Python script that calls /v1/audit with the HCC capability, parses missed_codes, and prints the RAF lift you would have captured if you coded the note correctly. Real CMS rates, no mocked numbers.
HCC gap capture is one of those workflows that sounds small until you try to build it: read a note, see which conditions the chart already has, see which conditions are documented but uncoded, score them against the CMS HCC model, and produce a number you can put in front of a CFO. The pieces are ICD-10 coding, code reconciliation, HCC mapping, RAF math, and revenue estimation; each one is a non-trivial project.
The AutoICD /v1/audit endpoint runs that whole pipeline server-side. You give it the clinical text and the codes that are already on the chart; it returns a structured response with confirmed, missed, unsupported, and upgrade buckets, plus totals. This post is a working Python script that turns that response into an HCC gap report. The script is under 60 lines including imports and pretty-printing.
What the audit returns
The HCC capability adds two pieces of information to each missed code: the HCC category and a RAF weight. The aggregator multiplies the total missed RAF by the CMS national base rate to give you an estimated_revenue_recovery in dollars. The defaults use the published 2026 base rate; pass a customer-specific base rate in the request to override.
The script
from autoicd import AutoICD
from autoicd.types import AuditRequest, AuditCode
client = AutoICD(api_key="sk_...")
note = """
68yo M, established CKD stage 3, type 2 diabetes mellitus,
chronic systolic heart failure on furosemide and lisinopril.
Today's visit for routine follow-up. Labs: A1c 7.4, eGFR 42.
Continues on metformin, carvedilol, atorvastatin.
"""
# Codes already on the chart for this encounter.
already_coded = [
AuditCode(code="E11.9", kind="icd10"),
]
response = client.audit(AuditRequest(
text=note,
codes=already_coded,
capabilities=["hcc"],
context={"patient": {"coverage": "medicare_advantage"}},
))
totals = response.totals
print(f"Coverage: {response.rates_used.source} HCC {response.rates_used.hcc_model}")
print(f"Confirmed: {totals.codes_confirmed}")
print(f"Missed: {totals.codes_missed}")
print(f"Missed RAF: {totals.missed_raf:.3f}")
print(f"Recovery: ${totals.estimated_revenue_recovery:,.2f}")
print()
print("Gap detail:")
for missed in response.missed:
raf = missed.raf_weight or 0.0
rev = missed.estimated_revenue or 0.0
cat = missed.hcc_category or "-"
print(f" {missed.code:8s} {cat:6s} RAF {raf:.3f} +${rev:,.2f} {missed.description}")
for span in missed.evidence[:1]:
print(f" evidence: {span.text!r}")What you get back
For a note like the one above, with only E11.9 already on the chart, the audit will surface CKD stage 3 and chronic systolic heart failure as missed HCC codes, attach the HCC category and RAF weight, and compute the per-code revenue using the published CMS rate. Output looks like:
Coverage: cms_national_2026 HCC v28
Confirmed: 1
Missed: 2
Missed RAF: 0.567
Estimated revenue recovery: $5,247.84
Gap detail:
N18.30 HCC329 RAF 0.127 +$1,176.42 Chronic kidney disease, stage 3 unspecified
evidence: "established CKD stage 3"
I50.22 HCC226 RAF 0.440 +$4,071.42 Chronic systolic (congestive) heart failure
evidence: "chronic systolic heart failure on furosemide and lisinopril"The numbers are illustrative; the actual RAF weights and revenue figures depend on the CMS HCC version and base rate in effect at audit time, both of which are surfaced on the response in `rates_used`. The category labels on missed codes (HCC329, HCC226 above) are the v28 codes; v22 categories use a different numbering and are returned when the request specifies hcc_model = "v22". See /audit for the full list of capabilities.
Why this matters at scale
Treat the per-encounter dollar figure as a directional signal, not a final billing number. Risk-adjustment is annualized; the same condition documented twelve months later does not stack. The gap-capture pipeline is therefore best run as a continuous process: surface the gap to the clinician at the point of documentation, then verify capture on the resulting claim. Running /v1/audit inside an EHR's encounter-close workflow is the cleanest place to do it.
Caveats and what comes next
A few things worth saying out loud before you put this into production:
- The HCC capability only counts codes that are clinically supported by the documentation. The reconciliation step uses an LLM call with extractive evidence, and codes with weak evidence get downgraded into the unsupported bucket rather than missed.
- The estimated revenue is a per-encounter calculation against the national base rate. Plan-specific rates, MA bid adjustments, and local geographic factors are not modelled here; pass a customer-specific base rate in the request body when you have one.
- RADV defense, specificity upgrades, and denial risk live behind the same endpoint with separate capability flags. Pricing tiers gate which capabilities a key can request; see /pricing for what each plan includes.