Denial auto-correction and rebill operator playbook
Outcome
Denials are auto-corrected via the registered handler chain and the resulting rebilled claim flows through the rest of the pipeline. Stuck or SKIPPED denials have a clear triage path.
Prerequisites
- Familiarity with Denials and appeals.
- Read of
billing.auto_correction_attempt.
Handler coverage
| CARC / PR code | Handler | Terminal action | Strategy |
|---|---|---|---|
CO-4 | Co4ModifierHandler | Rebill w/ missing modifier injected | MODIFIER_CORRECTION |
CO-16 | Co16MissingInfoHandler | Rebill w/ auth# (N479/M51/MA66) or SKIP for manual review (N350/M79/N381/M80) | MISSING_INFO_REBILL |
CO-18 | Co18DuplicateHandler | Write off + claim_relationship(DUPLICATE) | DUPLICATE_LINK |
CO-29 | Co29TimelyFilingHandler | File level-1 appeal with submission proof | TIMELY_FILING_APPEAL |
CO-97 | Co97AdjudicationHandler | Duplicate-match → write off, otherwise appeal | DUPLICATE_LINK / ADJUDICATION_APPEAL |
PR-1 / PR-2 / PR-3 | PatientLiabilityHandler | Resolve + emit patient.liability_transferred | PATIENT_BILLING |
Handlers that don't match simply skip. Handlers that match but can't
complete (no submission evidence for CO-29, no remit anchor for PR, etc.)
return SKIPPED — the attempt row is still written, which keeps the
success-rate view honest.
Event ordering
Tenant/org/correlation IDs thread from the inbound envelope.
PatientLiabilityHandler receives the event context via
patientLiabilityHandler.withExtra({ eventContext }) so its
liability-transfer events inherit the same tenantId / correlationId.
Idempotency guard
billing.auto_correction_attempt carries a partial unique index
ux_auto_correction_denial_handler_live on
(denial_id, handler_name) WHERE status <> 'FAILED'.
| Implication | Why |
|---|---|
FAILED attempts do not consume the slot | Retrying after a transient error is safe |
findByDenialAndHandler SKIPs on replay | We never rebill the same denial twice |
A second SUCCESS row is rejected | The data is already corrected; no manual override |
Dashboard: success-rate view
billing.v_auto_correction_success_rate aggregates attempts per CARC code:
SELECT carc_code, attempts, successes, failures, skipped,
success_rate, recovered_amount_cents
FROM billing.v_auto_correction_success_rate
ORDER BY success_rate DESC, attempts DESC;
The dashboard surfaces this via
GET /denials/auto-correction/stats?since=YYYY-MM-DD&until=YYYY-MM-DD.
The response wraps each row in the standard money envelope and adds a
rollup meta block (totalAttempts, overallSuccessRate,
totalRecoveredAmount). Empty windows return an empty array.
Common triage
"Why didn't CO-4 auto-correct?"
Check that:
- The claim has a
resolved_context_snapshotwithrendering_credentialfacility_address_state+payer_id.
rcm_reference.modifier_injection_rulehas a matching row (trigger_type,trigger_value, scope).- The missing modifier is not already on the line.
The handler SKIPs (not FAILs) when there's nothing to inject.
"Why didn't CO-29 file an appeal?"
The handler needs either a billing.claim_submission row or a pre-denial
billing.claim_status_history entry to produce a proof bundle. Without
one, it SKIPs so the denial stays OPEN for manual review.
"Why didn't PR-2 emit a liability event?"
PatientLiabilityHandler pulls PR adjustments from
billing.remittance_adjustment anchored to the denial's remit_claim_id
(claim-level or any of its remit_line rows). If the denial has no remit
anchor or the PR sum is zero, the handler SKIPs. Verify the 835 posted
cleanly before debugging the handler.
"Replay-safe?"
Yes. Re-emitting a claim.denied event re-runs the registry but every
matched handler sees a live auto_correction_attempt row and short-circuits
to SKIP. To actually retry a stuck denial (after fixing the underlying
data issue), delete the live attempt row or mark it FAILED. Or use the
manual trigger UI.
Validation
| Check | Expected |
|---|---|
auto_correction_attempt rows | One per (denial, handler) pair, status reflects outcome |
claim.rebilled + claim.corrected events | Emitted on SUCCESS with rebilledClaimId |
| Dashboard success rate | Visible per CARC |
Cross-references
- Manual auto-correction trigger for on-demand replay after upstream fixes.
- Denials and appeals for the denial workflow this engine plugs into.