Charge capture reconciliation engine for hospital revenue integrity. Expands documented clinical events into expected charges through an auditor-editable profile catalog, reconciles against posted charges, and reports revenue leakage and compliance exposure as two numbers that are never mixed.
- Charge capture leakage, care that was documented but never billed, silently costs hospitals an estimated 1% of net revenue. It hides in specific mechanical places: the infusion that ran 5.5 hours but was coded for 2, the ICU stay billed for fewer bed days than documented, the OR time posted short. ChargeTrace makes the expectation explicit: every documented event expands into the charges that should exist, and the delta is a workable finding with a dollar value.
- Findings come in five categories worked by different teams: MISSING and UNDER_QTY are leakage for finance to recover; UNEXPECTED (charge with no supporting documented event) and OVER_QTY are exposure for compliance to review; LATE drives rebills and DNFB aging. The two dollar directions are reported separately on principle, because netting overcharges against leakage understates both and turns an integrity report into a liability.
- On the synthetic corpus below the engine quantified $2.7M of leakage and $1.3M of exposure across 40,000 encounters in 0.65 seconds, with CI-gated 100% recall against the labeled defect taxonomy and zero false findings on clean charge lines.
Data disclosure: all encounters, events, and charges are synthetic, produced by a seeded generator in
datagen/with labeled defect injection. No real clinical or billing data appears anywhere. Every number and chart is reproducible with the commands below.
flowchart LR
A[Clinical utilization events<br/>ADT, infusion, OR, orders] --> E[Expectation expansion<br/>profile catalog + quantity rules]
B[Posted charges] --> M
E --> M[Reconciler<br/>per encounter + code]
M --> F1[Leakage findings<br/>MISSING / UNDER_QTY]
M --> F2[Exposure findings<br/>UNEXPECTED / OVER_QTY]
M --> F3[LATE findings<br/>rebill and DNFB drivers]
F1 & F2 & F3 --> DB[(SQLite findings)] --> S[SQL marts] --> C[Charts + integrity workqueue]
git clone <repo> && cd chargetrace
pip install -r requirements.txt -r requirements-dev.txt
PYTHONPATH=. python datagen/generate_data.py --encounters 40000
PYTHONPATH=. python -m chargetrace.run # reconcile, score, marts, charts
PYTHONPATH=. pytest tests/ -v # 16 known-answer tests| Findings | Dollars | |
|---|---|---|
| MISSING (never posted) | 1,329 | included in leakage |
| UNDER_QTY (short units, mostly infusion tiers) | 297 | included in leakage |
| Leakage total | 1,626 | $2.72M |
| UNEXPECTED (no supporting event) | 259 | included in exposure |
| OVER_QTY | 453 | included in exposure |
| Exposure total | 712 | $1.26M |
| LATE (correct but past the 3-day window) | 2,974 | rebill risk, not dollars |
Accuracy against the generator's labeled ground truth: 100% recall on every defect class (drop, under, over, late, phantom) with zero false findings on clean lines. As with any deterministic engine, that number is a regression contract rather than a boast: CI regenerates a corpus and fails the build if any class drops below 100% or a clean line gets flagged. What the claim means is "provably catches everything the profile catalog expresses, and provably does not cry wolf," and coverage grows with the catalog, which is the honest metric for this class of tool (ADR-0001).
The single most common charge capture miss is infusion additional-hour undercoding, and it is easy to get the expectation itself wrong. time_tier_units implements the CPT midpoint convention: units beyond the initial hour count per full hour, plus one more when the final partial hour reaches 30 minutes. So 1.2h earns no additional units, 1.5h earns one, 2.4h earns one, 5.5h earns five. Eight parametrized known-answer tests pin exactly these cases, alongside per-day bed charges, per-15-minute OR time with round-up, and multi-event aggregation before matching.
config/charge_profiles.yaml is written in the revenue integrity team's language: event type, department, expected charge codes, quantity rule, unit price. Growing coverage means writing profiles, not code, and the engine raises loudly on event types it has no profile for rather than silently skipping them, the same fail-loud convention as the sibling projects' rule catalogs.
- Charge-to-event line provenance. Matching is per encounter and code because most charge feeds cannot link a posted line to a specific documented event; the aggregation semantics are tested instead.
- NCCI bundling and payer-specific billing rules; this engine answers "was the care billed," and DenialGuard's territory begins where "was it billed correctly for this payer" starts.
- A worklist UI; the findings table with SQL marts is the integration surface, and the expected-loss ordering pattern from the sibling projects applies directly.
config/ expectation profile catalog (the auditor-editable surface)
chargetrace/ engine (expansion, matching, categories) + batch runner
sql/ revenue integrity marts (standalone SQL)
datagen/ seeded synthetic events + charges with labeled defects
tests/ 16 known-answer tests incl. CPT midpoint tiering
reports/ run report + charts regenerated from the findings db
docs/adr/ profiles-as-config and two-direction reporting rationale

