Payerworks writes and explains files; it sends nothing to a payer. Every file a route writes says transmitted: false, and is yours to send on your own connection. A test key is for invented data only, never a real patient.
An 835 is the payer’s remittance: what it paid, allowed and adjusted, claim by claim and line by line. POST /v1/remittances/explain reads it check by check, says each claim’s verdict in words, and checks that each check adds up to the claims it pays. POST /v1/denials/next-step says each adjustment as what the payer did, whether the patient may be billed, and the next step, with every deadline and where it comes from.
POST /v1/appeals/letter writes an appeal letter from the facts you send, by a fixed template, and never adds a code, an amount or a date you did not give. The routes after it work across several 835s: duplicates, deposits matched to checks, recoupments matched to the claims they take back, and an 835 split by payee or rendering provider.
New here? Start with A sandbox claim, to its 835.
Routes
- POST
/v1/remittances/explainExplain a remittance - POST
/v1/denials/next-stepThe next step on a denial - POST
/v1/appeals/letterWrite an appeal letter - POST
/v1/appeals/letter/checkCheck an edited letter - GET
/v1/codes/carc/{code}An adjustment reason code - GET
/v1/codes/group/{code}An adjustment group code - POST
/v1/remittances/duplicatesFind duplicate remittances - POST
/v1/remittances/reassociateMatch deposits to remittances - POST
/v1/remittances/splitSplit an 835 by payee or rendering provider - POST
/v1/remittances/recoupmentsMatch recoupments to the claims they take back - GET
/v1/filing-limitsPublished claim filing windows - GET
/v1/filing-limits/{payer}One payer’s filing window
Each route shows its call with curl and with the two clients, then its real answer, shortened, and the fields it holds. The client calls assume one client made once, const pw = new Payerworks() in JavaScript or pw = payerworks.Payerworks() in Python, each reading PAYERWORKS_API_KEY and PAYERWORKS_BASE_URL, and body is the request.json shown.
POST/v1/remittances/explain
Explain a remittance
An 835 claim by claim: what was paid, allowed and written off, the patient’s share, the verdict in words, each service line, every adjustment and remark code in words, and whether each check adds up to the claims it pays.
Request
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/remittances/explain" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.postRemittancesExplain(body);With the Python client
answer = pw.post_remittances_explain(body){
"x12": "ISA*00* *00* *ZZ*SAMPLEPAYER *ZZ*SAMPLEBILLER *261018*0900*^*00501*000000001*0*T*:~\nGS*HP*SAMPLEPAYER*SAMPLEBILLER*20261018*0900*1*X*005010X221A1~\nST*835*0001~\nBPR*I*1260*C*ACH*CCP*01*999999999*DA*123456*1234567890**01*999999999*DA*654321*20261018~\nTRN*1*EFT-20261018-01*1234567890~\nN1*PR*SAMPLE HEALTH PLAN*PI*99999~\nN1*PE*SAMPLE SLEEP DENTISTRY*XX*1234567893~\nLX*1~\nCLP*PW-1001*1*2900*1260*240*12*PAYER-CN-1*11~\nCAS*CO*45*1400~\nCAS*PR*2*240~\nNM1*QC*1*PATIENT*SAMPLE~\nDTM*472*20261001~\nSVC*HC:E0486*2900*1260~\nAMT*B6*1500~\nCLP*PW-1002*4*350*0*0*12*PAYER-CN-2*11~\nCAS*CO*50*350~\nNM1*QC*1*PATIENT*SECOND~\nDTM*472*20261002~\nSVC*HC:95800*350*0~\nLQ*HE*N115~\nSE*20*0001~\nGE*1*1~\nIEA*1*000000001~"
}{
"check": {
"number": "EFT-20261018-01",
"date": "20261018",
"amount": "1260",
"method_flag": "C",
"payer": "SAMPLE HEALTH PLAN",
"payee_ids": {
"npis": [
"1234567893"
],
"tins": []
}
},
"checks": [
{
"number": "EFT-20261018-01",
"date": "20261018",
"amount": "1260",
"method_flag": "C",
"payer": "SAMPLE HEALTH PLAN",
"payee_ids": {
"npis": [
"1234567893"
],
"tins": []
},
"references": [
"PW-1001",
"PW-1002"
],
"balance": {
"stated": 1260,
"claims_net": 1260,
"provider_adjustments_net": 0,
"variance": 0,
"ties": true,
"words": "The claims, less the provider-level adjustments, add up to the check."
},
"provider_adjustments": []
}
],
"claims": [
{
"reference": "PW-1001",
"payer_claim_number": "PAYER-CN-1",
"status_code": "1",
"status": "processed as primary",
"service_date": "20261001",
"billed": 2900,
"paid": 1260,
"allowed": 1500,
"patient_responsibility": 240,
"amounts": {
"billed": 2900,
"paid": 1260,
"allowed": 1500,
"allowed_basis": "stated",
"patient_responsibility": 240,
"patient_basis": "stated",
"adjustment": 1400,
"adjustment_basis": "stated",
"write_off": 1400,
"below_allowed": 0,
"not_paid": 0,
"shortfall": 0
},
"adjustments": [
{
"group": "CO",
"code": "45",
"amount": 1400,
"label": "Charge exceeds the fee schedule / contracted amount",
"category": "Underpayment",
"known": true,
"label_source": "paraphrase",
"patient_billable": false
}
],
"patient_adjustments": [
{
"group": "PR",
"code": "2",
"amount": 240,
"label": "Coinsurance amount",
"category": "Other",
"known": true,
"label_source": "paraphrase",
"patient_billable": true
}
],
"remark_codes": [],
"primary_reason": null,
"crossover_carrier": "",
"service_lines": [
{
"procedure_code": "E0486",
"modifiers": [],
"billed": 2900,
"paid": 1260,
"allowed": 1500
}
],
"check_number": "EFT-20261018-01",
"index": 0,
"claim_filing_indicator": "12",
"verdict": "paid",
"verdict_words": "Paid: the payer paid $1,260.00, the patient owes $240.00 (patient responsibility), and $1,400.00 is a contractual adjustment.",
"remarks": [],
"claim_adjustments": [
{
"group": "CO",
"code": "45",
"amount": 1400
},
{
"group": "PR",
"code": "2",
"amount": 240
}
],
"lines": [
{
"index": 0,
"procedure_code": "E0486",
"procedure_qualifier": "HC",
"modifiers": [],
"billed": 2900,
"paid": 1260,
"allowed": 1500,
"units_paid": null,
"service_date": null,
"service_date_end": null,
"line_control_number": "",
"adjustments": [],
"remarks": []
}
]
},
{
"reference": "PW-1002",
"payer_claim_number": "PAYER-CN-2",
"status_code": "4",
"status": "denied",
"service_date": "20261002",
"billed": 350,
"paid": 0,
"allowed": null,
"patient_responsibility": 0,
"amounts": {
"billed": 350,
"paid": 0,
"allowed": null,
"allowed_basis": null,
"patient_responsibility": 0,
"patient_basis": "stated",
"adjustment": 350,
"adjustment_basis": "stated",
"write_off": 0,
"below_allowed": 0,
"not_paid": 350,
"shortfall": null
},
"adjustments": [
{
"group": "CO",
"code": "50",
"amount": 350,
"label": "Non-covered: not deemed a medical necessity",
"category": "Medical Necessity",
"known": true,
"label_source": "paraphrase",
"patient_billable": false
}
],
"patient_adjustments": [],
"remark_codes": [
"N115"
],
"primary_reason": {
"code": "50",
"category": "Medical Necessity",
"words": "50, Non-covered: not deemed a medical necessity"
},
"crossover_carrier": "",
"service_lines": [
{
"procedure_code": "95800",
"modifiers": [],
"billed": 350,
"paid": 0,
"allowed": null
}
],
"check_number": "EFT-20261018-01",
"index": 1,
"claim_filing_indicator": "12",
"verdict": "denied",
"verdict_words": "Refused: the payer paid nothing. The refusing adjustment is CO-50 (Non-covered: not deemed a medical necessity).",
"remarks": [
{
"code": "N115",
"words": "The decision was based on a Local Coverage Determination (LCD).",
"label_source": "paraphrase"
}
],
"claim_adjustments": [
{
"group": "CO",
"code": "50",
"amount": 350
}
],
"lines": [
{
"index": 0,
"procedure_code": "95800",
"procedure_qualifier": "HC",
"modifiers": [],
"billed": 350,
"paid": 0,
"allowed": null,
"units_paid": null,
"service_date": null,
"service_date_end": null,
"line_control_number": "",
"adjustments": [],
"remarks": [
{
"code": "N115",
"words": "The decision was based on a Local Coverage Determination (LCD).",
"label_source": "paraphrase"
}
]
}
]
}
],
"provider_adjustments": [],
"groups": {
"CO": {
"name": "Contractual obligation",
"patient_billable": false,
"words": "An adjustment your agreement with the payer, or a regulation, requires. It is generally a write-off for you and is not billed to the patient."
},
"OA": {
"name": "Other adjustment",
"patient_billable": "depends",
"words": "Used when no other group applies, such as the effect of an earlier payer's payment. Whether the patient may be billed depends on the reason code beside it and on your agreement with the plan."
},
"PI": {
"name": "Payer-initiated reduction",
"patient_billable": false,
"words": "A reduction the payer made itself. It is not a patient responsibility amount, so it is not billed to the patient."
},
"PR": {
"name": "Patient responsibility",
"patient_billable": true,
"words": "An amount that may be billed to the patient, such as the deductible, coinsurance or copay."
}
}
}| Field | Type | What it is |
|---|---|---|
check | Check | |
checks | list of Check | One per check: an 835 pays each payee by its own. |
claims | list of RemittanceClaim | |
provider_adjustments | list of object | |
groups | Groups | Each adjustment group code the answer names (CO, OA, PI, PR), said once: its name, whether the patient may be billed, and its words. An adjustment carries its group code only. |
POST/v1/denials/next-step
The next step on a denial
An 835, or the claims as JSON: for each claim and each adjustment, what the payer did, whether the patient may be billed, the next step, the corrected claim fields to send, what to attach, and every deadline with where it comes from: a Medicare regulation with its section, the limits you send, or no date at all.
Request
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/denials/next-step" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.postDenialsNextStep(body);With the Python client
answer = pw.post_denials_next_step(body){
"x12": "ISA*00* *00* *ZZ*SAMPLEPAYER *ZZ*SAMPLEBILLER *261018*0900*^*00501*000000093*0*T*:~\nGS*HP*SAMPLEPAYER*SAMPLEBILLER*20261018*0900*93*X*005010X221A1~\nST*835*0001~\nBPR*I*80*C*ACH*CCP*01*999999999*DA*123456*1234567890**01*999999999*DA*654321*20261018~\nTRN*1*EFT-20261018-02*1234567890~\nN1*PR*SAMPLE HEALTH PLAN*PI*99999~\nN1*PE*SAMPLE FAMILY MEDICINE*XX*1234567893~\nLX*1~\nCLP*PW-2003*1*215*0**12*PAYER-CN-23*11~\nNM1*QC*1*PATIENT*SAMPLE****MI*W123456789~\nDTM*232*20261001~\nSVC*HC:99213*150*0~\nDTM*472*20261001~\nCAS*CO*16*150~\nLQ*HE*N290~\nSVC*HC:87880*65*0~\nDTM*472*20261001~\nCAS*CO*16*65~\nLQ*HE*N290~\nCLP*PW-2004*1*350*0**MB*PAYER-CN-24*11~\nNM1*QC*1*PATIENT*SECOND****MI*1EG4TE5MK73~\nDTM*232*20261002~\nSVC*HC:99214*350*0~\nDTM*472*20261002~\nCAS*CO*50*350~\nLQ*HE*N115~\nCLP*PW-2005*1*240*80*100*12*PAYER-CN-25*11~\nNM1*QC*1*PATIENT*THIRD****MI*W555555555~\nDTM*232*20261003~\nSVC*HC:99213*240*80~\nDTM*472*20261003~\nCAS*CO*45*60~\nCAS*PR*1*100~\nAMT*B6*180~\nCLP*PW-2006*1*150*0**12*PAYER-CN-26*11~\nNM1*QC*1*PATIENT*FOURTH****MI*W666666666~\nDTM*232*20260101~\nSVC*HC:99213*150*0~\nDTM*472*20260101~\nCAS*CO*29*150~\nSE*39*0001~\nGE*1*93~\nIEA*1*000000093~"
}{
"source": "x12",
"remittance_date": "2026-10-18",
"today": "2026-10-19",
"claims": [
{
"index": 0,
"reference": "PW-2003",
"payer_claim_number": "PAYER-CN-23",
"status_code": "1",
"status": "processed as primary",
"claim_filing_indicator": "12",
"medicare": null,
"service_date": "2026-10-01",
"remittance_date": "2026-10-18",
"notice_date": "2026-10-18",
"notice_date_source": "bpr_16",
"billed": 215,
"paid": 0,
"allowed": null,
"patient_responsibility": null,
"verdict": "denied",
"verdict_words": "Refused: the payer paid nothing. The refusing adjustment is CO-16 (Claim lacks information or has a submission/billing error). The claim status code says processed as primary, but the adjustments refused the money.",
"patient": {
"may_bill": 0,
"words": "Nothing on this remittance is the patient's to pay. The $215.00 under group CO (contractual) may not be billed to the patient."
},
"adjustments": [
{
"group": "CO",
"code": "16",
"amount": 150,
"label": "Claim lacks information or has a submission/billing error",
"category": "Missing Information",
"known": true,
"label_source": "paraphrase",
"patient_billable": false,
"line": 0,
"remedy": {
"key": "correct",
"action": "correct",
"label": "Correct the claim and send a replacement",
"words": "The payer says something on the claim was missing or wrong. Fix it and send a replacement claim (frequency code 7) that carries the payer's claim number. An appeal is not the route for this.",
"is_letter": false,
"letter_kind": null
}
},
{
"group": "CO",
"code": "16",
"amount": 65,
"label": "Claim lacks information or has a submission/billing error",
"category": "Missing Information",
"known": true,
"label_source": "paraphrase",
"patient_billable": false,
"line": 1,
"remedy": {
"key": "correct",
"action": "correct",
"label": "Correct the claim and send a replacement",
"words": "The payer says something on the claim was missing or wrong. Fix it and send a replacement claim (frequency code 7) that carries the payer's claim number. An appeal is not the route for this.",
"is_letter": false,
"letter_kind": null
}
}
],
"remarks": [
{
"code": "N290",
"words": "The rendering provider's primary identifier is missing, incomplete or invalid.",
"label_source": "paraphrase"
},
{
"code": "N290",
"words": "The rendering provider's primary identifier is missing, incomplete or invalid.",
"label_source": "paraphrase"
}
],
"next_step": {
"key": "correct",
"action": "correct",
"label": "Correct the claim and send a replacement",
"words": "The payer says something on the claim was missing or wrong. Fix it and send a replacement claim (frequency code 7) that carries the payer's claim number. An appeal is not the route for this.",
"is_letter": false,
"letter_kind": null
},
"corrected_claim": {
"frequency_code": "7",
"void_frequency_code": "8",
"claim_control_number": "PAYER-CN-23",
"fields": {
"claimInformation.claimFrequencyCode": "7",
"claimInformation.claimSupplementalInformation.claimControlNumber": "PAYER-CN-23"
},
"words": "Send a replacement claim through POST /v1/claims/837p with claimInformation.claimFrequencyCode \"7\" and claimInformation.claimSupplementalInformation.claimControlNumber \"PAYER-CN-23\" (the payer's claim number); it is written as CLM05-3 7 and…"
},
"send": [],
"deadlines": [
{
"kind": "timely_filing",
"date": null,
"date_us": null,
"days_left": null,
"window": null,
"counted_from": "the date of service",
"source": {
"kind": "none"
},
"words": "The payer's filing limit is stated in its contract or provider manual. Send facts.filing_limit_days to have the date computed."
}
]
},
{
"index": 1,
"reference": "PW-2004",
"payer_claim_number": "PAYER-CN-24",
"status_code": "1",
"status": "processed as primary",
"claim_filing_indicator": "MB",
"medicare": "fee_for_service",
"service_date": "2026-10-02",
"remittance_date": "2026-10-18",
"notice_date": "2026-10-18",
"notice_date_source": "bpr_16",
"billed": 350,
"paid": 0,
"allowed": null,
"patient_responsibility": null,
"verdict": "denied",
"verdict_words": "Refused: the payer paid nothing. The refusing adjustment is CO-50 (Non-covered: not deemed a medical necessity). The claim status code says processed as primary, but the adjustments refused the money.",
"patient": {
"may_bill": 0,
"words": "Nothing on this remittance is the patient's to pay. The $350.00 under group CO (contractual) may not be billed to the patient."
},
"adjustments": [
{
"group": "CO",
"code": "50",
"amount": 350,
"label": "Non-covered: not deemed a medical necessity",
"category": "Medical Necessity",
"known": true,
"label_source": "paraphrase",
"patient_billable": false,
"line": 0,
"remedy": {
"key": "necessity",
"action": "appeal",
"label": "Appeal with the clinical records",
"words": "The payer says the service was not medically necessary. Appeal with the clinical records that support it, the ones the payer's coverage policy asks for.",
"is_letter": true,
"letter_kind": "medical_necessity"
}
}
],
"remarks": [
{
"code": "N115",
"words": "The decision was based on a Local Coverage Determination (LCD).",
"label_source": "paraphrase"
}
],
"next_step": {
"key": "necessity",
"action": "appeal",
"label": "Appeal with the clinical records",
"words": "The payer says the service was not medically necessary. Appeal with the clinical records that support it, the ones the payer's coverage policy asks for.",
"is_letter": true,
"letter_kind": "medical_necessity",
"letter_facts": {
"payer_name": "SAMPLE HEALTH PLAN",
"patient_name": "SECOND PATIENT",
"payer_claim_number": "PAYER-CN-24",
"patient_control_number": "PW-2004",
"service_date": "2026-10-02",
"billed_amount": 350,
"remittance_date": "2026-10-18",
"adjustment_codes": [
"CO-50"
],
"remark_codes": [
"N115"
],
"claim_filing_indicator": "MB"
},
"letter_facts_missing": [
"member_id",
"provider_name"
]
},
"corrected_claim": null,
"send": [],
"deadlines": [
{
"kind": "redetermination",
"date": "2027-02-20",
"date_us": "02/20/2027",
"days_left": 124,
"window": {
"days": 120,
"presumed_receipt_days": 5
},
"counted_from": "receipt of the initial determination",
"counted_from_date": "2026-10-18",
"counted_from_date_source": "bpr_16",
"source": {
"kind": "regulation",
"citation": "42 CFR 405.942(a)",
"url": "https://www.ecfr.gov/current/title-42/section-405.942",
"verified_on": "10/19/2026",
"presumed_receipt_citation": "42 CFR 405.942(a)(1)"
},
"words": "A redetermination (the first level of appeal) is due within 120 days of receiving the initial determination, and receipt is presumed 5 days after the notice's date: 02/20/2027, counted from the payment date (BPR16), as the remittance advice…"
},
{
"kind": "reconsideration",
"date": null,
"date_us": null,
"days_left": null,
"window": {
"days": 180,
"presumed_receipt_days": 5
},
"counted_from": "receipt of the redetermination decision",
"source": {
"kind": "regulation",
"citation": "42 CFR 405.962(a)",
"url": "https://www.ecfr.gov/current/title-42/section-405.962",
"verified_on": "10/19/2026",
"presumed_receipt_citation": "42 CFR 405.962(a)(1)"
},
"words": "A later level: 180 days from receipt of the redetermination decision, receipt presumed 5 days after that decision's date (42 CFR 405.962(a); 42 CFR 405.962(a)(1)). Its date is known once that decision is."
}
]
}
],
"groups": {
"CO": {
"name": "Contractual obligation",
"patient_billable": false,
"words": "An adjustment your agreement with the payer, or a regulation, requires. It is generally a write-off for you and is not billed to the patient."
},
"OA": {
"name": "Other adjustment",
"patient_billable": "depends",
"words": "Used when no other group applies, such as the effect of an earlier payer's payment. Whether the patient may be billed depends on the reason code beside it and on your agreement with the plan."
},
"PI": {
"name": "Payer-initiated reduction",
"patient_billable": false,
"words": "A reduction the payer made itself. It is not a patient responsibility amount, so it is not billed to the patient."
},
"PR": {
"name": "Patient responsibility",
"patient_billable": true,
"words": "An amount that may be billed to the patient, such as the deductible, coinsurance or copay."
}
},
"deadline_rule": "A deadline is computed only from a regulation it cites (Medicare fee-for-service, by its filing indicator MA or MB), or from the limits you send as facts; otherwise its date is null and its words say the payer's contract or provider manual …"
}Shortened here: lists show their first two items and long strings are cut. The whole answer is this route’s example in /openapi.json.
| Field | Type | What it is |
|---|---|---|
source | one of "x12", "claims" | |
remittance_date | string or null | |
today | string | |
claims | list of NextStepClaim | |
groups | Groups | Each adjustment group code the answer names (CO, OA, PI, PR), said once: its name, whether the patient may be billed, and its words. An adjustment carries its group code only. |
deadline_rule | string |
POST/v1/appeals/letter
Write an appeal letter
An appeal letter written from the facts you send, by a fixed template for its kind. It never adds a code, an amount or a date you did not give. A fact the kind needs and you left out is named, and nothing is written.
Request
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/appeals/letter" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.postAppealsLetter(body);With the Python client
answer = pw.post_appeals_letter(body){
"kind": "timely_filing",
"facts": {
"payer_name": "Sample Health Plan",
"patient_name": "Sample Patient",
"member_id": "W666666666",
"payer_claim_number": "PAYER-CN-26",
"service_date": "2026-01-01",
"provider_name": "Sample Family Medicine",
"provider_npi": "1234567893",
"signer_name": "Sam Sample",
"original_filing_date": "2026-01-15",
"proof_reference": "the clearinghouse acceptance report dated 01/16/2026",
"filing_limit_days": 90
}
}{
"kind": "timely_filing",
"subject": "Request for reconsideration: timely filing denial",
"letter": "10/19/2026\n\nSample Health Plan\n\nRe: Request for reconsideration: timely filing denial\nPatient: Sample Patient\nMember ID: W666666666\nClaim number: PAYER-CN-26\nDate of service: 01/01/2026\n\nTo the appeals department:\n\nThis claim was denied as …",
"facts_used": [
{
"field": "payer_name",
"label": "Payer",
"value": "Sample Health Plan"
},
{
"field": "patient_name",
"label": "Patient",
"value": "Sample Patient"
}
],
"enclosures": [],
"guard": {
"passed": true
},
"transmitted": false
}Shortened here: lists show their first two items and long strings are cut. The whole answer is this route’s example in /openapi.json.
| Field | Type | What it is |
|---|---|---|
kind | string | |
subject | string | |
letter | string | The letter, as plain text. |
facts_used | list of FactUsed | |
enclosures | list of string | |
guard | LetterGuard | |
transmitted | the value false | Payerworks wrote the letter and sent it nowhere. |
POST/v1/appeals/letter/check
Check an edited letter
A letter you edited, held to the facts by the same rule: every code, amount and date it states, however it is written (a dollar sign or the word dollars, a written or numeric date, CO-50 or CARC 50, N115 or N 115), must be one the facts hold, and a placeholder left in is found.
Request
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/appeals/letter/check" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.postAppealsLetterCheck(body);With the Python client
answer = pw.post_appeals_letter_check(body){
"letter": "We ask that you reverse the denial of the claim for Sample Patient (CO-29), billed at $150.00.",
"facts": {
"patient_name": "Sample Patient",
"adjustment_codes": [
"CO-29"
]
}
}{
"guard": {
"passed": false,
"reason": "unknown_amount",
"value": "$150.00",
"words": "The letter states $150.00, which no fact holds. Add the amount to the facts, or take it out of the letter.",
"findings": [
{
"reason": "unknown_amount",
"value": "$150.00",
"words": "The letter states $150.00, which no fact holds. Add the amount to the facts, or take it out of the letter."
}
]
},
"facts_used": [
{
"field": "patient_name",
"label": "Patient",
"value": "Sample Patient"
},
{
"field": "adjustment_codes",
"label": "Adjustment codes",
"value": "CO-29"
}
]
}| Field | Type | What it is |
|---|---|---|
guard | LetterGuard | |
facts_used | list of FactUsed |
GET/v1/codes/carc/{code}
An adjustment reason code
A claim adjustment reason code in words, with its category, sent bare (45) or with its group (CO-45). A remark code sent here is named as one.
Request
curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/codes/carc/CO-16" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY"With the JavaScript client
const answer = await pw.getCodesCarcByCode("CO-16");With the Python client
answer = pw.get_codes_carc_by_code("CO-16"){
"code": "16",
"label": "Claim lacks information or has a submission/billing error",
"category": "Missing Information",
"known": true,
"label_source": "paraphrase",
"stopped_on": null,
"group": {
"code": "CO",
"known": true,
"name": "Contractual obligation",
"words": "An adjustment your agreement with the payer, or a regulation, requires. It is generally a write-off for you and is not billed to the patient.",
"patient_billable": false
},
"source": {
"list": "X12 claim adjustment reason codes",
"url": "https://x12.org/codes/claim-adjustment-reason-codes",
"read_on": "10/19/2026"
}
}| Field | Type | What it is |
|---|---|---|
code | string | |
label | string | |
category | string | |
known | boolean | |
label_source | the value "paraphrase" | |
stopped_on | string or null | |
group | object or null | |
source | object |
GET/v1/codes/group/{code}
An adjustment group code
A claim adjustment group code (CO, PR, OA, PI or CR) in words, and whether the patient may be billed an amount under it.
Request
curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/codes/group/PR" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY"With the JavaScript client
const answer = await pw.getCodesGroupByCode("PR");With the Python client
answer = pw.get_codes_group_by_code("PR"){
"code": "PR",
"known": true,
"name": "Patient responsibility",
"words": "An amount that may be billed to the patient, such as the deductible, coinsurance or copay.",
"patient_billable": true,
"patient_billable_words": "The patient may be billed this amount.",
"label_source": "paraphrase",
"source": {
"list": "X12 claim adjustment group codes",
"url": "https://x12.org/codes/claim-adjustment-group-codes",
"read_on": "10/19/2026"
}
}| Field | Type | What it is |
|---|---|---|
code | string | |
known | boolean | |
name | string | |
words | string | |
patient_billable | boolean or string or null | true, false, "depends" or null. |
patient_billable_words | string | |
label_source | the value "paraphrase" | |
source | object | |
valid_codes | list of string |
POST/v1/remittances/duplicates
Find duplicate remittances
Send several 835s and get back every check that is one payment sent more than once: the same trace number (TRN02) from the same originator (TRN03), never the trace alone. Each duplicate says whether the copies are the same remittance or differ in content, and a check with no trace is listed apart. Nothing is stored.
Request
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/remittances/duplicates" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.postRemittancesDuplicates(body);With the Python client
answer = pw.post_remittances_duplicates(body){
"remittances": [
"ISA*00* *00* *ZZ*SAMPLEPAYER *ZZ*SAMPLEBILLER *261018*0900*^*00501*000000001*0*T*:~\nGS*HP*SAMPLEPAYER*SAMPLEBILLER*20261018*0900*1*X*005010X221A1~\nST*835*0001~\nBPR*I*1260*C*ACH*CCP*01*999999999*DA*123456*1234567890**01*999999999*DA*654321*20261018~\nTRN*1*EFT-20261018-01*1234567890~\nN1*PR*SAMPLE HEALTH PLAN*PI*99999~\nN1*PE*SAMPLE SLEEP DENTISTRY*XX*1234567893~\nLX*1~\nCLP*PW-1001*1*2900*1260*240*12*PAYER-CN-1*11~\nCAS*CO*45*1400~\nCAS*PR*2*240~\nNM1*QC*1*PATIENT*SAMPLE~\nDTM*472*20261001~\nSVC*HC:E0486*2900*1260~\nAMT*B6*1500~\nCLP*PW-1002*4*350*0*0*12*PAYER-CN-2*11~\nCAS*CO*50*350~\nNM1*QC*1*PATIENT*SECOND~\nDTM*472*20261002~\nSVC*HC:95800*350*0~\nLQ*HE*N115~\nSE*20*0001~\nGE*1*1~\nIEA*1*000000001~",
"ISA*00* *00* *ZZ*SAMPLEPAYER *ZZ*SAMPLEBILLER *261018*0900*^*00501*000000001*0*T*:~\nGS*HP*SAMPLEPAYER*SAMPLEBILLER*20261018*0900*1*X*005010X221A1~\nST*835*0001~\nBPR*I*1260*C*ACH*CCP*01*999999999*DA*123456*1234567890**01*999999999*DA*654321*20261018~\nTRN*1*EFT-20261018-01*1234567890~\nN1*PR*SAMPLE HEALTH PLAN*PI*99999~\nN1*PE*SAMPLE SLEEP DENTISTRY*XX*1234567893~\nLX*1~\nCLP*PW-1001*1*2900*1260*240*12*PAYER-CN-1*11~\nCAS*CO*45*1400~\nCAS*PR*2*240~\nNM1*QC*1*PATIENT*SAMPLE~\nDTM*472*20261001~\nSVC*HC:E0486*2900*1260~\nAMT*B6*1500~\nCLP*PW-1002*4*350*0*0*12*PAYER-CN-2*11~\nCAS*CO*50*350~\nNM1*QC*1*PATIENT*SECOND~\nDTM*472*20261002~\nSVC*HC:95800*350*0~\nLQ*HE*N115~\nSE*20*0001~\nGE*1*1~\nIEA*1*000000001~"
]
}{
"files": 2,
"checks": [
{
"file": 0,
"check": 0,
"trace": "EFT-20261018-01",
"originator": "1234567890",
"amount": 1260,
"date": "2026-10-18",
"method": "ACH",
"payer": "SAMPLE HEALTH PLAN",
"claims": 2,
"duplicate_of": null
},
{
"file": 1,
"check": 0,
"trace": "EFT-20261018-01",
"originator": "1234567890",
"amount": 1260,
"date": "2026-10-18",
"method": "ACH",
"payer": "SAMPLE HEALTH PLAN",
"claims": 2,
"duplicate_of": {
"file": 0,
"check": 0,
"trace": "EFT-20261018-01",
"originator": "1234567890",
"amount": 1260,
"date": "2026-10-18",
"method": "ACH",
"payer": "SAMPLE HEALTH PLAN",
"claims": 2
}
}
],
"duplicates": [
{
"trace": "EFT-20261018-01",
"originator": "1234567890",
"matched_on": [
"TRN02",
"TRN03"
],
"identical": true,
"copies": [
{
"file": 0,
"check": 0,
"trace": "EFT-20261018-01",
"originator": "1234567890",
"amount": 1260,
"date": "2026-10-18",
"method": "ACH",
"payer": "SAMPLE HEALTH PLAN",
"claims": 2
},
{
"file": 1,
"check": 0,
"trace": "EFT-20261018-01",
"originator": "1234567890",
"amount": 1260,
"date": "2026-10-18",
"method": "ACH",
"payer": "SAMPLE HEALTH PLAN",
"claims": 2
}
],
"words": "2 checks carry the same trace (TRN02 EFT-20261018-01) from the same originator (TRN03 1234567890), and their content is the same: one remittance sent twice. Post it once."
}
],
"no_trace": [],
"words": "1 payment is in these files more than once, matched by trace (TRN02) and originator (TRN03).",
"reads": "Only the files sent in this call: nothing is stored, and no payer or bank is asked."
}| Field | Type | What it is |
|---|---|---|
files | integer | |
checks | list of RemitCheck | Each check, with duplicate_of naming the first copy. |
duplicates | list of object | |
no_trace | list of object | |
words | string | |
reads | string |
POST/v1/remittances/reassociate
Match deposits to remittances
Send 835s and the deposits from your bank (the amount and effective date, and the trace number or description when your bank gives them). Each deposit is matched to one check by its trace number first, then by a trace in the description, then by amount and date only when exactly one check and one deposit fit, and every match says what agreed and what did not. Unmatched deposits and checks are listed. No bank description is repeated back.
Request
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/remittances/reassociate" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.postRemittancesReassociate(body);With the Python client
answer = pw.post_remittances_reassociate(body){
"remittances": [
"ISA*00* *00* *ZZ*SAMPLEPAYER *ZZ*SAMPLEBILLER *261018*0900*^*00501*000000001*0*T*:~\nGS*HP*SAMPLEPAYER*SAMPLEBILLER*20261018*0900*1*X*005010X221A1~\nST*835*0001~\nBPR*I*1260*C*ACH*CCP*01*999999999*DA*123456*1234567890**01*999999999*DA*654321*20261018~\nTRN*1*EFT-20261018-01*1234567890~\nN1*PR*SAMPLE HEALTH PLAN*PI*99999~\nN1*PE*SAMPLE SLEEP DENTISTRY*XX*1234567893~\nLX*1~\nCLP*PW-1001*1*2900*1260*240*12*PAYER-CN-1*11~\nCAS*CO*45*1400~\nCAS*PR*2*240~\nNM1*QC*1*PATIENT*SAMPLE~\nDTM*472*20261001~\nSVC*HC:E0486*2900*1260~\nAMT*B6*1500~\nCLP*PW-1002*4*350*0*0*12*PAYER-CN-2*11~\nCAS*CO*50*350~\nNM1*QC*1*PATIENT*SECOND~\nDTM*472*20261002~\nSVC*HC:95800*350*0~\nLQ*HE*N115~\nSE*20*0001~\nGE*1*1~\nIEA*1*000000001~",
"ISA*00* *00* *ZZ*SAMPLEPAYER *ZZ*SAMPLEBILLER *261018*0900*^*00501*000000093*0*T*:~\nGS*HP*SAMPLEPAYER*SAMPLEBILLER*20261018*0900*93*X*005010X221A1~\nST*835*0001~\nBPR*I*80*C*ACH*CCP*01*999999999*DA*123456*1234567890**01*999999999*DA*654321*20261018~\nTRN*1*EFT-20261018-02*1234567890~\nN1*PR*SAMPLE HEALTH PLAN*PI*99999~\nN1*PE*SAMPLE FAMILY MEDICINE*XX*1234567893~\nLX*1~\nCLP*PW-2003*1*215*0**12*PAYER-CN-23*11~\nNM1*QC*1*PATIENT*SAMPLE****MI*W123456789~\nDTM*232*20261001~\nSVC*HC:99213*150*0~\nDTM*472*20261001~\nCAS*CO*16*150~\nLQ*HE*N290~\nSVC*HC:87880*65*0~\nDTM*472*20261001~\nCAS*CO*16*65~\nLQ*HE*N290~\nCLP*PW-2004*1*350*0**MB*PAYER-CN-24*11~\nNM1*QC*1*PATIENT*SECOND****MI*1EG4TE5MK73~\nDTM*232*20261002~\nSVC*HC:99214*350*0~\nDTM*472*20261002~\nCAS*CO*50*350~\nLQ*HE*N115~\nCLP*PW-2005*1*240*80*100*12*PAYER-CN-25*11~\nNM1*QC*1*PATIENT*THIRD****MI*W555555555~\nDTM*232*20261003~\nSVC*HC:99213*240*80~\nDTM*472*20261003~\nCAS*CO*45*60~\nCAS*PR*1*100~\nAMT*B6*180~\nCLP*PW-2006*1*150*0**12*PAYER-CN-26*11~\nNM1*QC*1*PATIENT*FOURTH****MI*W666666666~\nDTM*232*20260101~\nSVC*HC:99213*150*0~\nDTM*472*20260101~\nCAS*CO*29*150~\nSE*39*0001~\nGE*1*93~\nIEA*1*000000093~"
],
"deposits": [
{
"id": "bank-1",
"amount": 1260,
"effective_date": "2026-10-19",
"trace_number": "EFT-20261018-01",
"originator": "1234567890"
},
{
"id": "bank-2",
"amount": 80,
"effective_date": "2026-10-19",
"description": "SAMPLE HEALTH PLAN HCCLAIMPMT EFT-20261018-02"
}
]
}{
"matches": [
{
"deposit": {
"index": 0,
"id": "bank-1",
"amount": 1260,
"effective_date": "2026-10-19"
},
"check": {
"file": 0,
"check": 0,
"trace": "EFT-20261018-01",
"originator": "1234567890",
"amount": 1260,
"date": "2026-10-18",
"method": "ACH",
"payer": "SAMPLE HEALTH PLAN",
"claims": 2
},
"matched_by": "trace",
"matched_on": [
"TRN02",
"TRN03"
],
"agrees": {
"amount": true,
"date": false,
"originator": true,
"weekdays_from_payment_date": 1
},
"words": "Matched by the trace number (TRN02): check EFT-20261018-01 of 10/18/2026 (file 1), agreeing on TRN02, TRN03, BPR02. But the date differs: the deposit settled 10/19/2026 and the check's payment date (BPR16) is 10/18/2026, 1 weekday apart."
}
],
"unmatched_deposits": [
{
"deposit": {
"index": 1,
"id": "bank-2",
"amount": 80,
"effective_date": "2026-10-19"
},
"words": "Not matched: the deposit carries no trace number, its description names none of the checks' traces, and no single check has its amount on its date."
}
],
"unmatched_checks": [
{
"check": {
"file": 1,
"check": 0,
"trace": "EFT-20261018-02",
"originator": "1234567890",
"amount": 80,
"date": "2026-10-18",
"method": "ACH",
"payer": "SAMPLE HEALTH PLAN",
"claims": 4
},
"words": "Unmatched: no deposit among those sent matches this check. CAQH CORE 370 has the plan release the 835 within 3 business days of the EFT's effective entry date either way, so the money may not have settled yet."
}
],
"counts": {
"checks": 2,
"deposits": 2,
"matched": 1
},
"words": "1 of 2 deposits matched to a check; 1 deposit and 1 check unmatched.",
"how": "Matched by trace number first (TRN02, with TRN03 when the deposit names its originator), then by a trace printed in the bank's description (at least 6 characters, as a whole word), then by amount and date only where neither carries a trace …",
"sources": [
{
"name": "CMS, EFT and ERA: Payment Remittance Reassociation Basics (June 2022)",
"url": "https://www.cms.gov/files/document/eft-and-era-payment-remittance-reassociation-basics.pdf",
"read_on": "10/19/2026"
},
{
"name": "CAQH CORE, EFT and ERA Operating Rules Overview",
"url": "https://www.caqh.org/sites/default/files/core/phase-iii/policy-rules/EFT-ERA_Rules_Overview.pdf",
"read_on": "10/19/2026"
}
],
"reads": "Only the files and deposits sent in this call: nothing is stored, no bank description is repeated back, and no payer or bank is asked."
}Shortened here: lists show their first two items and long strings are cut. The whole answer is this route’s example in /openapi.json.
| Field | Type | What it is |
|---|---|---|
matches | list of object | |
unmatched_deposits | list of object | |
unmatched_checks | list of object | |
counts | object | |
words | string | |
how | string | |
sources | list of object | |
reads | string |
POST/v1/remittances/split
Split an 835 by payee or rendering provider
Send an 835 paid to one tax ID and get one file per payee (the provider the payer grouped the claims under) or per rendering NPI. Each file’s payment total is recomputed from its own claims and provider-level adjustments and read back to check it balances, the parts add up to the original check, and each keeps its trace. An adjustment that names no single group gets a file of its own, never a guessed one, and a value holding a delimiter is refused, never rewritten. Send the interchange control number you track for the first file (each further file takes the next): the original’s own number is never reused. The files are yours to load; nothing is sent.
Request
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/remittances/split" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.postRemittancesSplit(body);With the Python client
answer = pw.post_remittances_split(body){
"x12": "ISA*00* *00* *ZZ*SAMPLEPAYER *ZZ*SAMPLEBILLER *261018*0900*^*00501*000000094*0*T*:~\nGS*HP*SAMPLEPAYER*SAMPLEBILLER*20261018*0900*94*X*005010X221A1~\nST*835*0001~\nBPR*I*330*C*ACH*CCP*01*999999999*DA*123456*1234567890**01*999999999*DA*654321*20261018~\nTRN*1*EFT-20261018-03*1234567890~\nDTM*405*20261018~\nN1*PR*SAMPLE HEALTH PLAN*PI*99999~\nN1*PE*SAMPLE FAMILY MEDICINE*FI*123456789~\nLX*1~\nTS3*1234567893*11*20261231*1*240~\nCLP*PW-2101*1*240*180*60*12*PAYER-CN-31*11~\nCAS*PR*2*60~\nNM1*QC*1*PATIENT*SAMPLE****MI*W123456789~\nNM1*82*1*SAMPLE*SAM****XX*1234567893~\nSVC*HC:99213*240*180~\nDTM*472*20261001~\nLX*2~\nTS3*1999999992*11*20261231*1*200~\nCLP*PW-2102*1*200*170*30*12*PAYER-CN-32*11~\nCAS*PR*2*30~\nNM1*QC*1*PATIENT*SECOND****MI*W987654321~\nNM1*82*1*SAMPLE*SARA****XX*1999999992~\nSVC*HC:99214*200*170~\nDTM*472*20261002~\nPLB*123456789*20261231*WO:PAYER-CN-32 PW-2102*20~\nSE*24*0001~\nGE*1*94~\nIEA*1*000000094~",
"by": "payee",
"control_number": 1
}{
"by": "payee",
"transmitted": false,
"files": [
{
"group": {
"by": "payee",
"key": "1234567893",
"stated": true
},
"claims": 1,
"checks": [
{
"trace": "EFT-20261018-03",
"original_amount": 330,
"amount": 180,
"claims_paid": 180,
"provider_adjustments": 0
}
],
"ties": true,
"isa_control": "000000001",
"x12": "ISA*00* *00* *ZZ*SAMPLEPAYER *ZZ*SAMPLEBILLER *261019*1500*^*00501*000000001*0*T*:~GS*HP*SAMPLEPAYER*SAMPLEBILLER*20261019*1500*1*X*005010X221A1~ST*835*0001~BPR*I*180*C*ACH*CCP*01*999999999*DA*123456*1234567890**01*99…",
"words": "The claims the payer grouped under provider 1234567893."
},
{
"group": {
"by": "payee",
"key": "1999999992",
"stated": true
},
"claims": 1,
"checks": [
{
"trace": "EFT-20261018-03",
"original_amount": 330,
"amount": 150,
"claims_paid": 170,
"provider_adjustments": 20
}
],
"ties": true,
"isa_control": "000000002",
"x12": "ISA*00* *00* *ZZ*SAMPLEPAYER *ZZ*SAMPLEBILLER *261019*1500*^*00501*000000002*0*T*:~GS*HP*SAMPLEPAYER*SAMPLEBILLER*20261019*1500*2*X*005010X221A1~ST*835*0001~BPR*I*150*C*ACH*CCP*01*999999999*DA*123456*1234567890**01*99…",
"words": "The claims the payer grouped under provider 1999999992."
}
],
"checks": [
{
"trace": "EFT-20261018-03",
"original_amount": 330,
"parts_total": 330,
"parts_add_up": true,
"original_ties": true,
"claims_kept": true,
"provider_adjustments": [
{
"code": "WO",
"reference": "PAYER-CN-32 PW-2102",
"amount": 20,
"provider": "123456789",
"group": "1999999992",
"assigned_by": "its reference (PLB03-2) names a claim in this group"
}
]
}
],
"balanced": true,
"how": "In each file, each check's BPR02 is recomputed as its claims' payments (CLP04) less its provider-level adjustments (PLB), BPR03 C, or D with the amount unsigned when that is negative. The LX numbers are renumbered, a TS3 whose loop is cut s…",
"notes": [],
"words": "Split into 2 files by payee; every file ties and the parts add up to the original."
}Shortened here: lists show their first two items and long strings are cut. The whole answer is this route’s example in /openapi.json.
| Field | Type | What it is |
|---|---|---|
by | one of "payee", "rendering_npi" | |
transmitted | the value false | Payerworks wrote the files and sent them nowhere. |
files | list of object | |
checks | list of object | |
balanced | boolean | |
how | string | |
notes | list of string | |
words | string |
POST/v1/remittances/recoupments
Match recoupments to the claims they take back
Send a set of 835s. Every provider-level adjustment is named, and each takeback is matched to the paid claim its reference names in the files you sent: by payer claim number, then patient control number, then amount when only one earlier payment at the same payer fits. Unmatched takebacks are listed, with what each payer took back and what the files show still carried forward. It says what was taken and from which claim, never whether it should have been.
Request
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/remittances/recoupments" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.postRemittancesRecoupments(body);With the Python client
answer = pw.post_remittances_recoupments(body){
"remittances": [
"ISA*00* *00* *ZZ*SAMPLEPAYER *ZZ*SAMPLEBILLER *261018*0900*^*00501*000000001*0*T*:~\nGS*HP*SAMPLEPAYER*SAMPLEBILLER*20261018*0900*1*X*005010X221A1~\nST*835*0001~\nBPR*I*1260*C*ACH*CCP*01*999999999*DA*123456*1234567890**01*999999999*DA*654321*20261018~\nTRN*1*EFT-20261018-01*1234567890~\nN1*PR*SAMPLE HEALTH PLAN*PI*99999~\nN1*PE*SAMPLE SLEEP DENTISTRY*XX*1234567893~\nLX*1~\nCLP*PW-1001*1*2900*1260*240*12*PAYER-CN-1*11~\nCAS*CO*45*1400~\nCAS*PR*2*240~\nNM1*QC*1*PATIENT*SAMPLE~\nDTM*472*20261001~\nSVC*HC:E0486*2900*1260~\nAMT*B6*1500~\nCLP*PW-1002*4*350*0*0*12*PAYER-CN-2*11~\nCAS*CO*50*350~\nNM1*QC*1*PATIENT*SECOND~\nDTM*472*20261002~\nSVC*HC:95800*350*0~\nLQ*HE*N115~\nSE*20*0001~\nGE*1*1~\nIEA*1*000000001~",
"ISA*00* *00* *ZZ*SAMPLEPAYER *ZZ*SAMPLEBILLER *261018*0900*^*00501*000000095*0*T*:~\nGS*HP*SAMPLEPAYER*SAMPLEBILLER*20261018*0900*95*X*005010X221A1~\nST*835*0001~\nBPR*I*50*C*ACH*CCP*01*999999999*DA*123456*1234567890**01*999999999*DA*654321*20261102~\nTRN*1*EFT-20261102-01*1234567890~\nDTM*405*20261102~\nN1*PR*SAMPLE HEALTH PLAN*PI*99999~\nN1*PE*SAMPLE SLEEP DENTISTRY*XX*1234567893~\nLX*1~\nCLP*PW-1006*1*150*150*0*12*PAYER-CN-6*11~\nNM1*QC*1*PATIENT*FIFTH****MI*W777777777~\nSVC*HC:99213*150*150~\nDTM*472*20261020~\nPLB*1234567893*20261231*WO:PAYER-CN-1 PW-1001*100~\nSE*13*0001~\nGE*1*95~\nIEA*1*000000095~"
]
}{
"adjustments": [
{
"check": {
"file": 1,
"check": 0,
"trace": "EFT-20261102-01",
"originator": "1234567890",
"amount": 50,
"date": "2026-11-02",
"method": "ACH",
"payer": "SAMPLE HEALTH PLAN",
"claims": 1
},
"provider_identifier": "1234567893",
"code": "WO",
"label": "Overpayment recovery",
"kind": "claim_recoupment",
"known": true,
"label_source": "paraphrase",
"amount": 100,
"effect": "withheld",
"reference": "PAYER-CN-1 PW-1001",
"takeback": true,
"match": {
"matched_by": "payer_claim_number",
"matched_on": "CLP07",
"token": "PAYER-CN-1",
"claim": {
"reference": "PW-1001",
"payer_claim_number": "PAYER-CN-1",
"status_code": "1",
"paid": 1260,
"billed": 2900,
"service_date": "2026-10-01",
"check": {
"file": 0,
"check": 0,
"trace": "EFT-20261018-01",
"originator": "1234567890",
"amount": 1260,
"date": "2026-10-18",
"method": "ACH",
"payer": "SAMPLE HEALTH PLAN",
"claims": 2
}
},
"reversed_in_files": null
},
"matched": true,
"ambiguous": false,
"words": "WO (overpayment recovery): $100.00 withheld from check EFT-20261102-01 of 11/02/2026 (file 2), matched to claim PW-1001 paid $1,260.00 on check EFT-20261018-01 of 10/18/2026 (file 1), by the payer claim number PAYER-CN-1 (CLP07) in its refe…"
}
],
"reversals": [],
"payers": [
{
"payer": "SAMPLE HEALTH PLAN",
"originator": "1234567890",
"checks": 2,
"adjustments": 1,
"taken_back": 100,
"matched_taken_back": 100,
"unmatched_taken_back": 0,
"returned": 0,
"reversed_on_claims": 0,
"other_withheld": 0,
"other_added": 0,
"forwarding_balance_net": 0,
"open_balance": 0,
"words": "SAMPLE HEALTH PLAN: $100.00 taken back from claims by provider-level adjustment, $100.00 of it matched to a claim in these files and $0.00 not."
}
],
"counts": {
"checks": 2,
"adjustments": 1,
"takebacks": 1,
"matched": 1,
"unmatched": 0,
"reversals": 0
},
"words": "1 takeback by provider-level adjustment, 1 matched to a claim in these files; 0 claim reversals.",
"how": "Each provider-level adjustment is named by X12's list of PLB reason codes and matched to the one claim payment its reference (PLB03-2) names at the same payer: the payer claim control number (CLP07) first, then the patient control number (C…",
"source": {
"list": "X12 provider adjustment reason codes (code list 967)",
"url": "https://x12.org/codes/provider-adjustment-reason-codes",
"read_on": "10/19/2026"
},
"reads": "Only the files sent in this call: nothing is stored, and no payer is asked."
}Shortened here: lists show their first two items and long strings are cut. The whole answer is this route’s example in /openapi.json.
| Field | Type | What it is |
|---|---|---|
adjustments | list of object | |
reversals | list of object | |
payers | list of object | |
counts | object | |
words | string | |
how | string | |
source | object | |
reads | string |
GET/v1/filing-limits
Published claim filing windows
The claim filing window each payer in Payerworks’ table publishes, read on its own pages on 09/10/2026, and Medicare fee-for-service’s from 42 CFR 424.44. Search with q, and add date_of_service to count the last day where the payer states one figure; a payer whose pages leave the window to its agreement says so, with no day. Your agreement with the payer governs over a published window, and nothing here is a payer’s answer about a claim.
Request
curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/filing-limits?q=select&date_of_service=2026-03-15" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY"With the JavaScript client
const answer = await pw.getFilingLimits({ query: {"q":"select","date_of_service":"2026-03-15"} });With the Python client
answer = pw.get_filing_limits(query={"q": "select", "date_of_service": "2026-03-15"}){
"total": 1,
"data": [
{
"slug": "select-health",
"payer": "Select Health",
"applies_to": null,
"answer": "window",
"answer_words": "A published window",
"words": "1 year from the date of service.",
"window": {
"years": 1
},
"date_of_service": "2026-03-15",
"last_day": "2027-03-15",
"last_day_words": "File by 03/15/2027.",
"source": {
"url": null,
"read_on": "09/10/2026",
"words": "Read on the payer's own pages on 09/10/2026; the page's address was not kept."
}
}
],
"note": "A payer's own agreement with you, or the plan's document, governs over a published window; a secondary claim's window may differ. Nothing here is a payer's answer about a claim."
}| Field | Type | What it is |
|---|---|---|
total | integer | |
data | list of FilingLimit | |
note | string |
GET/v1/filing-limits/{payer}
One payer’s filing window
One payer by its slug from GET /v1/filing-limits, or by its name, with every plan line it publishes a window for; add date_of_service to count the last day. A payer with no window on record is 404 no_filing_limit_on_record, never a window.
Request
curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/filing-limits/select-health?date_of_service=2026-03-15" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY"With the JavaScript client
const answer = await pw.getFilingLimitsByPayer("select-health", { query: {"date_of_service":"2026-03-15"} });With the Python client
answer = pw.get_filing_limits_by_payer("select-health", query={"date_of_service": "2026-03-15"}){
"payer": "Select Health",
"data": [
{
"slug": "select-health",
"payer": "Select Health",
"applies_to": null,
"answer": "window",
"answer_words": "A published window",
"words": "1 year from the date of service.",
"window": {
"years": 1
},
"date_of_service": "2026-03-15",
"last_day": "2027-03-15",
"last_day_words": "File by 03/15/2027.",
"source": {
"url": null,
"read_on": "09/10/2026",
"words": "Read on the payer's own pages on 09/10/2026; the page's address was not kept."
}
}
],
"note": "A payer's own agreement with you, or the plan's document, governs over a published window; a secondary claim's window may differ. Nothing here is a payer's answer about a claim."
}| Field | Type | What it is |
|---|---|---|
payer | string | |
data | list of FilingLimit | |
note | string |
Reading the answer
- A claim’s verdict comes from the money and the refusing codes, never from CLP02 alone: a refusal often arrives as CLP02 1 with nothing paid.
primary_reasonisnullwhen nothing refused money. A contractual write-off (CO-45) on a paid claim is not why a claim was refused.- A deadline cites its source or has no date: Medicare’s own windows from the regulation for Medicare claims, else the limits you send, else
null. - A file with several checks says each check apart, and no single number or total for the file.
Troubleshooting
not_an_835(422): The file is not an 835. A 277CA or a 999 goes to POST /v1/acknowledgments/explain.is_a_remark_code(400): N115 and other remark codes (RARCs) are not adjustment reason codes; the CARC lookup refuses them and says so.missing_facts(422): An appeal letter needs facts that were not sent; the answer lists them, and nothing is written.letter_refused(422): A fact held a placeholder in brackets, which the letter guard refuses.no_filing_limit_on_record(404): No published filing window is on record for that payer: ask the payer or read your agreement. A window is never guessed.
Every refusal these routes may answer, with its status. Any route may also answer the codes every route shares, on Every error.
| Code | Status | What it means |
|---|---|---|
deposits_required | 400 | Send the bank's deposits as deposits: a list of { amount, effective_date, trace_number?, originator?, description?, id? }. |
invalid_code | 400 | An adjustment reason code is one to four digits, or a letter and digits (such as 45, A1 or B13), optionally after its group. |
invalid_date_of_service | 400 | date_of_service is a day, YYYY-MM-DD. |
invalid_group_code | 400 | A claim adjustment group code is two letters, such as CO or PR. |
invalid_letter | 400 | Send the letter's text as letter, at most 20000 characters. |
invalid_payer | 400 | Name the payer by its slug from GET /v1/filing-limits, or by its name. |
invalid_split_by | 400 | by is "payee" (the 2000 loop's provider, TS301) or "rendering_npi" (each claim's rendering provider, NM1*82). |
is_a_remark_code | 400 | This is a remittance advice remark code (RARC), not a claim adjustment reason code (CARC). |
no_remittance | 400 | Send an 835 as x12, or the claims as a list in claims. |
no_remittances | 400 | Send the 835s as remittances: a list of X12 files, each the file's text. |
no_x12 | 400 | Send the file's text as x12. |
too_many_claims | 400 | At most 100 claims a call. |
two_inputs | 400 | Send either x12 (an 835) or claims (a list), not both. |
unknown_letter_kind | 400 | kind is one of the letter kinds the answer lists, with the facts each requires. |
no_filing_limit_on_record | 404 | No published filing window is on record for this payer. Ask the payer, or read your agreement with it; GET /v1/filing-limits lists every payer that has one. |
too_many_remittances | 413 | More was sent than one call reads across its files (at most 50 files, 1000 checks, 5000 claims, 5000 provider-level adjustments and 2000 deposits): the error names the unit and the limit. Send fewer in each call. |
invalid_claim_facts | 422 | One or more claims or facts are not usable; nothing was decided. Each problem is listed with its field. |
invalid_deposits | 422 | One or more deposits are not usable; nothing was matched. Each problem is listed with its field. |
invalid_letter_facts | 422 | One or more facts are not usable; nothing was written. Each is listed with its field. |
letter_refused | 422 | The letter was not written: a fact holds a placeholder in brackets, which the guard refuses. |
missing_facts | 422 | The letter needs facts that were not sent; the answer lists them. |
mixed_functional_groups | 422 | This interchange holds functional groups of different kinds: send each kind in its own interchange, so no part of it goes unread. |
not_an_835 | 422 | This is not an 835 remittance. |
value_holds_a_delimiter | 422 | A value in the file holds one of the characters Payerworks writes as a delimiter (* : ^ ~), so it cannot be copied unchanged; nothing was written. |
Code lists
Any one code in words: GET /v1/codes/carc/{code}, GET /v1/codes/group/{code}.
A remittance claim’s verdict
paid, partially_paid, denied, applied_to_patient, nothing_payable, reversal, reversal_and_repaid, forwarded, pending
The next steps on a denial
| Step | What to do | In words |
|---|---|---|
correct | Correct the claim and send a replacement | The payer says something on the claim was missing or wrong. Fix it and send a replacement claim (frequency code 7) that carries the payer's claim number. An appeal is not the route for this. |
attach | Send what the payer asked for | The remark codes say what the payer is missing. Send it, on a replacement claim with a PWK segment naming the attachment, or enclosed with an appeal, as the payer's instructions say. |
dispute | Check the payment against your contract | The payer allowed less than your charge. That is normal under a contract; it is an underpayment only if the allowed amount is below your contracted rate for this service. If it is, ask the payer to reprocess the claim at the contracted amount. |
timely | Appeal with proof of timely filing | The payer says the claim was filed after its limit. If you filed in time, appeal with your proof of filing, such as the acceptance acknowledgment (999 or 277CA) for the original claim and its date. A replacement claim cannot cure a filing limit. |
duplicate | Find the original claim before sending anything | The payer already holds a claim for this service. Find the original claim and its payment first: sending again makes more duplicates. Appeal only if this is a different service, and say what makes it different. |
discovery | Find the coverage active on the date of service | The payer says the patient was not covered on the date of service. Check eligibility for that date, find the plan that was active, and file the claim to it. |
cob | Bill the payers in the right order | The payer says another plan pays first. Confirm the order of benefits with the patient and the plans, file to the primary, then file to this payer as secondary with the primary's payment. If the patient has no other coverage, tell the payer so it can correct its record. Payerworks does not write a secondary claim yet. |
auth | Appeal with the authorization | The payer says an authorization was missing or did not cover the service. If you had one, appeal with its number. If you did not, ask the payer whether it grants one after the service. |
necessity | Appeal with the clinical records | The payer says the service was not medically necessary. Appeal with the clinical records that support it, the ones the payer's coverage policy asks for. |
non_covered | Check the benefit, then appeal or bill the patient | The payer calls the service not covered. Check the patient's benefits for it: if the plan covers it, appeal with that evidence. If it does not, whether the patient may be billed depends on the group code (PR yes, CO no) and on any notice the patient signed in advance. |
recoupment | Review the takeback | The payer took back money it had paid on this claim. Read the reason it gives; if you disagree, dispute the recoupment in writing and ask for the payment to be reinstated. |
contract_cut | Read the remittance before deciding | The payer reduced the charge to its contracted amount and paid nothing more. That can be the deductible, a coverage denial or an underpayment, and only the remittance's patient responsibility, group codes and remarks say which. Read them, then bill the patient, appeal, or dispute the allowed amount. |
decide | Read the denial and decide | The code alone does not decide the remedy. Read the remark codes and the payer's notes, then correct the claim, appeal it, or write it off. |
post_payment | Post the payment | The payer paid the claim. Post the payment and the contractual write-off; bill the patient only the patient responsibility. |
bill_patient | Bill the patient | The payer processed the claim and put the allowed amount on the patient (deductible, coinsurance or copay). Nothing was refused, so there is nothing to appeal: bill the patient. |
post_correction | Post the reversal, then the new payment | The payer reversed its earlier payment and paid the claim again. Post the reversal against the original payment, then post the new payment. |
await_new_decision | Wait for the new decision | The payer withdrew an earlier decision that paid nothing. No money moves; a new decision usually follows on a later remittance. |
forwarded | Wait for the next payer | The payer processed the claim and sent it on to the next payer itself. Do not file it to that payer by hand; wait for its own remittance. |
wait | Wait, or ask the payer | The payer has not finished this claim. Wait for its decision, or ask the payer where it stands (a claim status request, a 276). |
new_claim | Send a new claim | The payer says the claim could not be processed (remark MA130), so it has no appeal rights. Send a NEW claim (frequency code 1) with the information completed or corrected. |
resend | Fix the rejection and send it again | The claim was rejected at the payer's front end and never entered adjudication. Fix what the rejection names and send it again as a new claim (frequency code 1). |
post_no_payment | Post the adjudication: nothing is payable | The payer finished the claim and nothing is payable: the contractual or prior-payer adjustments account for the whole charge. Post it with its write-off. Check the allowed amount against your contract only if you believe it is wrong. |
paid_short | Ask the payer to pay its own allowed amount | The payer paid less than its own allowed amount less the patient's share, and gave no reason beyond the contractual adjustment. Ask it to pay the rest, or to say why it did not. The underpayment letter states it from allowed_amount, paid_amount and patient_responsibility_amount; no contracted rate is needed. |
necessity_patient | Appeal, or bill the patient | The payer refused the service as not medically necessary and put it on the patient (group PR), which a payer does when the patient signed a notice in advance that the plan might not pay. Two doors are open: appeal with the clinical records if the service was necessary, or bill the patient under that notice. |
timely_medicare | Ask for an exception to the time limit | Medicare does not treat a finding that a claim was filed late as an initial determination, so it cannot be appealed (42 CFR 405.926(n)). The only route is an exception to the filing limit under 42 CFR 424.44(b), such as a late filing caused by an error of CMS or its contractor, asked of your Medicare contractor with the evidence. If the claim was in fact filed in time, ask the contractor to correct its error. |
duplicate_medicare | Ask for a reopening, not an appeal | Medicare processes a denial of a claim as a duplicate as a clerical error, by reopening rather than by redetermination (42 CFR 405.980(a)(3)). Find the original claim first; if this is a different service, ask your Medicare contractor to reopen the claim and say what makes it different. |
The appeal letter kinds
| Kind | Subject | Facts it needs |
|---|---|---|
timely_filing | Request for reconsideration: timely filing denial | payer_name, patient_name, member_id, payer_claim_number, service_date, provider_name, signer_name, original_filing_date |
authorization_on_file | Request for reconsideration: authorization on file | payer_name, patient_name, member_id, payer_claim_number, service_date, provider_name, signer_name, authorization_number |
records_enclosed | Records enclosed for review of this claim | payer_name, patient_name, member_id, payer_claim_number, service_date, provider_name, signer_name, enclosures |
underpayment | Request for reconsideration: payment below the amount due | payer_name, patient_name, member_id, payer_claim_number, service_date, provider_name, signer_name, allowed_amount, paid_amount |
not_a_duplicate | Request for reconsideration: not a duplicate claim | payer_name, patient_name, member_id, payer_claim_number, service_date, provider_name, signer_name, distinguishing_facts |
other_coverage | Request for reconsideration: coordination of benefits | payer_name, patient_name, member_id, payer_claim_number, service_date, provider_name, signer_name, other_coverage_status |
medical_necessity | Request for reconsideration: medical necessity | payer_name, patient_name, member_id, payer_claim_number, service_date, provider_name, signer_name, clinical_summary, enclosures |
recoupment | Dispute of a recoupment | payer_name, patient_name, member_id, payer_claim_number, service_date, provider_name, signer_name, recoupment_amount, recoupment_date |
reconsideration | Request for reconsideration of the appeal decision | payer_name, patient_name, member_id, payer_claim_number, service_date, provider_name, signer_name, prior_appeal_date, reconsideration_reason |
Example
remittances.mjs runs every step below against the API with a test key. It needs payerworks.mjs and sample-data.mjs beside it.
// Payerworks example: remittances and denials, explained claim by claim. Docs: https://payerworks.com/docs/remittances
//
// Run it with Node 18 or later, with the JavaScript client (https://payerworks.com/sdk/payerworks.mjs)
// and sample-data.mjs beside this file:
// PAYERWORKS_API_KEY=pw_test_... PAYERWORKS_BASE_URL=<the base URL shown with your key> node remittances.mjs
// A test key and invented data only. Payerworks sends nothing to a payer: every file it writes is yours.
import { pathToFileURL } from 'node:url';
import { Payerworks, PayerworksError } from './payerworks.mjs';
import { SENDER_ID, claimEndingIn, refusalOf } from './sample-data.mjs';
/** @param {Payerworks} pw */
export async function run(pw) {
// 1. A paid claim and a denied one (a member ID ending in DENY), answered by the sandbox payer with an 835.
const sandbox = await pw.postSandboxClaims({ sender_id: SENDER_ID, claims: [claimEndingIn(''), claimEndingIn('DENY')] });
const the835 = sandbox.files.remittance_835;
// 2. The 835 claim by claim: paid, allowed, the patient's share and the verdict in words.
const explained = await pw.postRemittancesExplain({ x12: the835 });
// 3. Each denial as the next step, with every deadline and its source.
const next = await pw.postDenialsNextStep({ x12: the835 });
// 4. One adjustment reason code in words.
const carc = await pw.getCodesCarcByCode('50');
// 5. The failure path: N115 is a remark code, not an adjustment reason, and the lookup says so.
const refused = await refusalOf(() => pw.getCodesCarcByCode('N115'), { PayerworksError });
return {
verdicts: Object.fromEntries(explained.claims.map((c) => [c.reference, c.verdict])),
denied_claims: next.claims.filter((c) => c.reference === 'EX-DENY').length,
carc_50: carc.label,
refused,
};
}
// Run as a script: node <this file>, with PAYERWORKS_API_KEY and PAYERWORKS_BASE_URL set.
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
console.log(JSON.stringify(await run(new Payerworks()), null, 2));
}
{
"verdicts": {
"EX-PAID": "paid",
"EX-DENY": "denied"
},
"denied_claims": 1,
"carc_50": "Non-covered: not deemed a medical necessity",
"refused": {
"status": 400,
"code": "is_a_remark_code",
"request_id": "req_5"
}
}