Commercial auth workflow and EOB posting
Outcome
Commercial payer auths route to the right channel (EDI 278, portal, manual). Commercial EOB posting splits patient-responsibility correctly into deductible/coinsurance and tags carrier write-offs.
Prerequisites
- Familiarity with
config.payer_program_configandauth_workflow. See Onboarding a payer + program config. - Read of
billing.remittance_adjustmentfor write-off audit.
Auth workflow routing
Commercial payers often publish authorizations through proprietary portals (UHC, Availity, Aetna) or accept only phone / fax. Running every submit through the X12 278 generator was forging EDI payloads the payer never asked for.
AuthRequestService.submit consults
config.payer_program_config.auth_workflow and picks one of three
branches:
auth_workflow | Behaviour |
|---|---|
EDI_278 (default) | generate278() + markSubmitted('SUBMITTED') with full X12 payload. |
PORTAL | Skip 278 generation, markSubmitted('MANUAL_PENDING'), submitted_x12 = NULL. Operator submits via auth_portal_url. |
MANUAL | Same as PORTAL but no URL. Fax / phone / paper. |
When a payer has no payer_program_config row at all, the resolver returns
null and the service falls back to EDI_278 — this keeps pre-config
Medicaid payers on the 278 path without requiring a seed update.
Onboard a commercial payer to PORTAL
Insert (or UPSERT) a
config.payer_program_configrow with:Field Value auth_workflow'PORTAL'auth_portal_url'<payer's authorizations URL>'(CHECK constraint rejects NULL)auth_turnaround_days<payer SLA>Existing DRAFT auth requests for that payer will switch paths on the next
submitcall. In-flightSUBMITTEDrequests continue their existing lifecycle.
To return to EDI_278:
UPDATE config.payer_program_config
SET auth_workflow = 'EDI_278', auth_portal_url = NULL
WHERE payer_id = '<payer-id>';
Commercial EOB posting
RemittancePoster reads rcm_master.payer.payer_type via
resolvePayerContext. For commercial payers it does two things differently
from Medicaid/Medicare:
| What | Commercial | Medicaid/Medicare |
|---|---|---|
| PR adjustments split | PR-1 → DEDUCTIBLE, PR-2 → COINSURANCE, other PR → PATIENT_RESPONSIBILITY | Single PATIENT_RESPONSIBILITY bucket |
| CO-45 rows tag | adjustment_source = 'COMMERCIAL_CONTRACT' | NULL |
This split lets carrier write-off reports filter via ix_remit_adj_source.
Carrier write-off by payer this quarter
SELECT p.name AS payer,
SUM(a.amount_cents) / 100.0 AS contractual_writeoff_usd
FROM billing.remittance_adjustment a
JOIN billing.remittance_claim rc
ON rc.remit_claim_id = a.remit_claim_id
JOIN billing.remittance r
ON r.remit_id = rc.remit_id
JOIN rcm_master.payer p
ON p.payer_id = r.payer_id
WHERE a.adjustment_source = 'COMMERCIAL_CONTRACT'
AND r.received_at >= date_trunc('quarter', now())
GROUP BY p.name
ORDER BY contractual_writeoff_usd DESC;
Non-CO-45 CO rows (CO-97 duplicate, CO-29 timely filing, etc.) remain
adjustment_source = NULL — they are denials, not contractual write-offs,
and belong to the denial-management flow.
Validation
| Check | Expected |
|---|---|
payer_program_config.auth_workflow for the payer | matches the operational reality (EDI_278 / PORTAL / MANUAL) |
| Commercial 835 posts | PR splits into DEDUCTIBLE / COINSURANCE rows |
| CO-45 rows on commercial | adjustment_source = COMMERCIAL_CONTRACT |
| Auth submit on PORTAL workflow | auth_request.status = MANUAL_PENDING, submitted_x12 NULL |
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Submit still generates 278 for a PORTAL payer | payer_program_config.auth_workflow not flipped | Update to PORTAL and confirm with psql. |
| Operator reports no portal URL | auth_portal_url NULL | Required for PORTAL — set it (CHECK constraint enforces). |
| PR rows post as single PATIENT_RESPONSIBILITY for commercial | payer.payer_type is wrong | Update rcm_master.payer.payer_type to a commercial value. |
| CO-45 not tagged | Migration 094 not applied for tenant | Run migration fan-out. |
Cross-references
- Authorization workflow + 278 lifecycle UI.
- Receivables (835) workspace for daily posting flow.