Payer Underpayment Audit
Ticket BILL-2291, from Dana Whitfield, Billing Manager, Riverside Orthopedics & Sports Medicine
We think several of our payers are underpaying us. I need the Q3 appeal report for our four contracted payers: Summit Mutual Health Plan, Lakeshore Benefit Trust, Harborview Health Alliance (our Medicaid plan) and Crescent Care Health. That's every service line with a date of service from July 1 through September 30, 2024, what each contract says we should have been allowed, what the payer paid, how much we can go back for, and a worklist of the claims to appeal with each payer's filing deadline.
Last quarter we only appealed Summit. That batch is in
data/reference_case/withapproved_report.json, which Summit's network rep reviewed and paid in full. Build this quarter's report the same way.I'm in and out of meetings. Ask me what you need, but I can answer a few questions, not dozens. Make them count.
Deliverable
Write solve(files) in solution.mjs. files maps each path under data/ to its text contents, including the files in data/reference_case/. The function returns the Q3 report described under Output.
The graders run solve on other Q3 datasets with the same layout, generated with different claims, fee schedule rates and payer mistakes. Those datasets have no data/reference_case/ folder, so solve must not depend on it.
Working on this challenge
- Questions. Dana decides what goes into the appeal batch and knows the practice's agreements with its payers, and much of that isn't written down in the folder. Ask her with
vibrometer ask "your question". You have 7 questions, she answers only what you ask, and a vague question gets a question back. - Follow-up. Run
vibrometer updatefrom time to time. Anything new from Dana arrives in your folder with aCHANGE.md. The graders always test the latest requirements.
Input files
data/contracts/anddata/fee_schedules/: each payer's contract and fee schedules.data/era/: the payers' remittances (X12 835).data/pm_export/: the Q3 charge exports from the practice management system.- Some files in
data/have nothing to do with this.
Standards
- ASC X12 835 Health Care Claim Payment/Advice, version 5010 (implementation guide 005010X221A1), including the interchange envelope and its delimiters, the claim status codes, the payment date and the provider-level adjustments.
- Claim Adjustment Group Codes, Claim Adjustment Reason Codes (CARC) and Remittance Advice Remark Codes (RARC), the X12 code lists used in
CASandLQ. - CMS Medicare Physician Fee Schedule: the MULT PROC and BILAT SURG indicators of the RVU file, the multiple surgery and bilateral surgery payment rules in the Medicare Claims Processing Manual, Chapter 12, and the facility and non-facility settings by place of service.
Output
{
lines: [
{ payer: "SMHP", claimId: "RO2407160328", dos: "2024-07-16", cpt: "29881", modifiers: ["RT"], units: 1,
billed: 1271, expected: 596.46, paid: 467.63, patientResp: 119.29, sequestration: 9.54, priorPayer: 0,
underpaid: 0, reason: null },
// ...
],
totals: {
underpaid: 0, underpaidLines: 0,
byPayer: {
SMHP: { underpaid: 0, underpaidLines: 0, byReason: { BUNDLING: 0, MPPR: 0, BILATERAL: 0, FEE_SCHEDULE: 0 } },
LBT: { /* same shape */ }, HHA: { /* same shape */ }, CCH: { /* same shape */ }
}
},
skipped: [{ payer: "CCH", claimId: "RO2407190629", why: "DENIED" } /* , ... */],
appeals: [{ payer: "LBT", claimId: "RO2407010208", remitDate: "2024-07-18", deadline: "2024-10-16", underpaid: 42.96, reasons: ["BILATERAL"] } /* , ... */]
}
The totals above only show the shape. approved_report.json has the same shape, with zeros for the payers that it doesn't cover.
payer is one of SMHP (Summit Mutual Health Plan), LBT (Lakeshore Benefit Trust), HHA (Harborview Health Alliance) and CCH (Crescent Care Health).
| Field | Type | Meaning |
|---|---|---|
claimId |
string | our claim ID, as in the PM export |
dos |
string | date of service, YYYY-MM-DD |
cpt |
string | procedure code as billed |
modifiers |
string[] | the line's distinct modifiers, sorted ascending |
units |
number | units billed |
billed |
number | billed charge |
expected |
number | the amount that the payer's contract allows for the line |
paid |
number | the payer's payment for the line |
patientResp |
number | member cost sharing |
sequestration |
number | the sequestration reduction |
priorPayer |
number | the primary payer's part, on claims where the payer is secondary |
underpaid |
number | the amount to appeal for the line, never negative |
reason |
string or null | appeal queue: BUNDLING, MPPR, BILATERAL or FEE_SCHEDULE, or null when underpaid is 0 |
why |
string | NO_REMITTANCE, REVERSED or DENIED |
remitDate |
string | the remittance date that the appeal deadline counts from, YYYY-MM-DD |
deadline |
string | the last day to file the appeal under the payer's contract, YYYY-MM-DD |
reasons |
string[] | the distinct reason values of the claim's underpaid lines, sorted ascending |
lineshas one entry for every reported service line. Sort bypayer, thenclaimId,dos,cptand the modifiers joined with commas, all as plain ascending string comparisons.- Every money field is a number in dollars, rounded to the cent. Compute in whole cents so that sums are exact.
totals.underpaidandtotals.underpaidLinescover all lines.totals.byPayerhas an entry for each of the four payers, and eachbyReasonhas all four keys.skippedlists the claims that are in the batch but can't be reported line by line. Sort it bypayer, thenclaimId.appealshas one entry per claim to appeal, withunderpaidas the claim's total. Sort it bydeadline, thenpayer, thenclaimId.
The solution has to be one file that imports nothing except node: built-ins. It gets no file system or network access, and every input arrives through files.