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.
What a patient may owe, figured from what you send: an estimate from a 271 and the allowed amounts you expect, a Good Faith Estimate laid out with the elements the rule requires, the replacement window for equipment, and income against the HHS poverty guideline. POST /v1/price-transparency/validate checks a hospital’s standard charges file against the published template.
Routes
- POST
/v1/estimatesEstimate a patient’s share - POST
/v1/estimates/good-faithLay out a Good Faith Estimate - POST
/v1/dme/replacement-windowWhen equipment may be replaced - POST
/v1/financial-assistance/fplIncome against the poverty guideline - POST
/v1/price-transparency/validateCheck a hospital standard charges file
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/estimates
Estimate a patient’s share
A patient’s share and the plan’s, line by line, from a 271 (X12 or JSON) and the allowed amounts you send, at most 50 lines. You say the network level and, where a copay is stated, how it applies (copay_rule): a 271 says neither. The lines draw on the deductible and the out-of-pocket amount left in the order sent; for one member, a family amount left the 271 states bounds the member’s, and since a 271 does not say whether a family deductible is embedded or aggregate, the answer says which it read and what the other would mean. Where the out-of-pocket limit caps a line, its deductible and coinsurance are cut to add up to the share. Every input names where it came from: the 271, you, or not stated. A benefit the 271 did not state stops that line rather than reading as zero, unless you supply it, and no allowed amount is ever supplied for you: Payerworks holds no prices. The payer’s own processing decides what the patient owes.
Request
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/estimates" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.postEstimates(body);With the Python client
answer = pw.post_estimates(body){
"x12": "ISA*00* *00* *ZZ*SAMPLEPAYER *ZZ*SAMPLEBILLER *261018*0900*^*00501*000000091*0*T*:~\nGS*HB*SAMPLEPAYER*SAMPLEBILLER*20261018*0900*91*X*005010X279A1~\nST*271*0001*005010X279A1~\nBHT*0022*11*ELIG-0001*20261018*0900~\nHL*1**20*1~\nNM1*PR*2*SAMPLE HEALTH PLAN*****PI*99999~\nHL*2*1*21*1~\nNM1*1P*2*SAMPLE FAMILY MEDICINE*****XX*1234567893~\nHL*3*2*22*0~\nTRN*2*ELIG-0001*9SAMPLEBIL~\nNM1*IL*1*PATIENT*SAMPLE****MI*W123456789~\nREF*6P*G7700*SAMPLE EMPLOYER GROUP~\nDMG*D8*19800214*F~\nDTP*346*D8*20260101~\nEB*1*IND*30*PR*SAMPLE PPO~\nEB*C*IND*30***23*1500*****Y~\nEB*C*IND*30***29*600*****Y~\nEB*G*IND*30***23*6000*****Y~\nEB*G*IND*30***29*4100*****Y~\nEB*C*FAM*30***23*3000*****Y~\nEB*C*FAM*30***29*1800*****Y~\nEB*A*IND*30*****.2****Y~\nEB*C*IND*30***23*3000*****N~\nEB*C*IND*30***29*3000*****N~\nEB*A*IND*30*****.4****N~\nEB*B*IND*98****25*****Y~\nEB*A*IND*98*****.4****N~\nEB*1*IND*62********Y~\nSE*27*0001~\nGE*1*91~\nIEA*1*000000091~",
"network_level": "in_network",
"date_of_service": "2026-10-20",
"lines": [
{
"reference": "L1",
"allowed_amount": 180
},
{
"reference": "L2",
"allowed_amount": 900
}
]
}{
"kind": "estimate",
"source": "x12",
"member_index": 0,
"payer": {
"name": "SAMPLE HEALTH PLAN",
"id": "99999"
},
"plan": {
"name": "SAMPLE PPO",
"begins": "2026-01-01",
"ends": ""
},
"date_of_service": "2026-10-20",
"network_level": "in_network",
"coverage_level": "individual",
"insurance_type": null,
"coverage": {
"state": "active",
"active_on_date": "yes"
},
"lines": [
{
"index": 0,
"reference": "L1",
"service_type": "30",
"procedure_code": null,
"allowed_amount": 180,
"inputs": {
"allowed_amount": {
"value": 180,
"source": "caller",
"words": "You sent it (lines[0].allowed_amount)."
},
"copay": {
"value": null,
"source": "not_stated",
"words": "The 271 did not state a copay for this line, and you sent none."
},
"deductible_remaining_before": {
"value": 600,
"source": "271",
"scope": "plan",
"service_type": "30",
"network_tier": "in_network",
"coverage_level": "individual",
"insurance_type": null,
"words": "The 271, plan level (30), in network, individual, remaining amount stated."
},
"deductible_family_remaining_before": {
"value": 1800,
"source": "271",
"scope": "plan",
"service_type": "30",
"network_tier": "in_network",
"coverage_level": "family",
"insurance_type": null,
"words": "The 271, plan level (30), in network, family, remaining amount stated."
},
"deductible_family_design": {
"value": "not_stated",
"source": "not_stated",
"words": "The 271 has no field saying whether the family deductible is embedded or aggregate; it is read as embedded, so the member owes toward it no more than the lesser of the individual and family amounts left."
},
"out_of_pocket_remaining_before": {
"value": 4100,
"source": "271",
"scope": "plan",
"service_type": "30",
"network_tier": "in_network",
"coverage_level": "individual",
"insurance_type": null,
"words": "The 271, plan level (30), in network, individual, remaining amount stated."
}
},
"deductible_applied": 180,
"copay_applied": 0,
"coinsurance_amount": 0,
"patient_share": 180,
"plan_share": 0,
"out_of_pocket_reached": false,
"not_stated": [],
"why": [],
"prior_auth_required": null
},
{
"index": 1,
"reference": "L2",
"service_type": "30",
"procedure_code": null,
"allowed_amount": 900,
"inputs": {
"allowed_amount": {
"value": 900,
"source": "caller",
"words": "You sent it (lines[1].allowed_amount)."
},
"copay": {
"value": null,
"source": "not_stated",
"words": "The 271 did not state a copay for this line, and you sent none."
},
"deductible_remaining_before": {
"value": 420,
"source": "271",
"scope": "plan",
"service_type": "30",
"network_tier": "in_network",
"coverage_level": "individual",
"insurance_type": null,
"words": "The 271, plan level (30), in network, individual, remaining amount stated."
},
"deductible_family_remaining_before": {
"value": 1620,
"source": "271",
"scope": "plan",
"service_type": "30",
"network_tier": "in_network",
"coverage_level": "family",
"insurance_type": null,
"words": "The 271, plan level (30), in network, family, remaining amount stated."
},
"deductible_family_design": {
"value": "not_stated",
"source": "not_stated",
"words": "The 271 has no field saying whether the family deductible is embedded or aggregate; it is read as embedded, so the member owes toward it no more than the lesser of the individual and family amounts left."
},
"coinsurance_percent": {
"value": 20,
"source": "271",
"scope": "plan",
"service_type": "30",
"network_tier": "in_network",
"coverage_level": null,
"insurance_type": null,
"words": "The 271, plan level (30), in network."
},
"out_of_pocket_remaining_before": {
"value": 3920,
"source": "271",
"scope": "plan",
"service_type": "30",
"network_tier": "in_network",
"coverage_level": "individual",
"insurance_type": null,
"words": "The 271, plan level (30), in network, individual, remaining amount stated."
}
},
"deductible_applied": 420,
"copay_applied": 0,
"coinsurance_amount": 96,
"patient_share": 516,
"plan_share": 384,
"out_of_pocket_reached": false,
"not_stated": [],
"why": [],
"prior_auth_required": null
}
],
"totals": {
"allowed_amount": 1080,
"patient_share": 696,
"plan_share": 384,
"deductible_applied": 600,
"copay_applied": 0,
"coinsurance_amount": 96
},
"figured": true,
"not_stated": [],
"assumptions": [
"The 271 states both an individual ($600.00) and a family ($1800.00) deductible left and does not say whether the family deductible is embedded or aggregate. This estimate reads it as embedded (the member’s own amount caps what the member ow…"
],
"notes": [
"An estimate from the benefits the payer stated and the allowed amounts you sent, before the payer adjudicates the claim. The payer’s own processing decides what the patient owes. Payerworks holds no prices and keeps nothing it was sent.",
"The network level is yours: a 271 says what the plan pays at each benefit tier, never whether this provider participates with the plan."
],
"eligibility_notes": [
"A remaining amount is what was left on the day the payer answered.",
"In network and out of network are the plan’s benefit tiers as the payer stated them (EB12): what the plan pays at each tier. A 271 does not say whether this provider participates with the plan."
],
"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 | the value "estimate" | |
source | string | |
member_index | integer | |
payer | object | |
plan | object | |
date_of_service | string or null | |
network_level | string | |
coverage_level | string | |
insurance_type | string or null | |
coverage | object | |
lines | list of object | |
totals | object or null | Null when any line could not be figured. |
figured | boolean | |
not_stated | list of object | |
assumptions | list of string | |
notes | list of string | |
eligibility_notes | list of string | |
refusal | object | When the 271 itself is a refusal: the explainer’s reasons. |
transmitted | the value false |
POST/v1/estimates/good-faith
Lay out a Good Faith Estimate
A Good Faith Estimate for an uninsured or self-pay patient, with every element 45 CFR 149.610 lists, in order and each named by its paragraph: the patient, the primary service, the items grouped by provider with their codes and expected charges, the amount at which the patient may dispute the bill, the disclaimers, and when it is due by the business days you state. It is your document to issue, give and keep; Payerworks issues, delivers and keeps nothing.
Request
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/estimates/good-faith" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.postEstimatesGoodFaith(body);With the Python client
answer = pw.post_estimates_good_faith(body){
"issued_on": "2026-10-19",
"uninsured_reason": "not_filing_claim",
"patient": {
"first_name": "SAMPLE",
"last_name": "PATIENT",
"date_of_birth": "1980-02-14"
},
"primary_service": {
"description": "A new patient visit and a sleep study",
"scheduled_date": "2026-11-09"
},
"providers": [
{
"id": "p1",
"role": "convening",
"name": "Sample Family Medicine",
"npi": "1234567893",
"tin": "12-3456789",
"locations": [
{
"state": "IL"
}
]
},
{
"id": "p2",
"role": "co_provider",
"name": "Sample Sleep Lab",
"npi": "1999999992",
"tin": "987654321",
"locations": [
{
"state": "IL"
}
]
}
],
"items": [
{
"provider_id": "p1",
"description": "New patient visit",
"service_code": "99203",
"service_code_type": "CPT",
"diagnosis_codes": [
"G47.33"
],
"expected_charge": 180
},
{
"provider_id": "p2",
"description": "Home sleep apnea test",
"service_code": "G0399",
"service_code_type": "HCPCS",
"diagnosis_codes": [
"G47.33"
],
"expected_charge": 350
}
],
"separately_scheduled": [
{
"description": "A follow-up visit to read the results"
}
],
"timing": {
"business_days_until_service": 15
}
}{
"document": "good_faith_estimate",
"provider_document": true,
"statement": "This is the provider’s Good Faith Estimate: the convening provider or facility issues it in writing, gives it to the patient and keeps it with the medical record (45 CFR 149.610(e)(1), (f)(1)). Payerworks laid it out from what you sent; it …",
"rule": {
"cite": "45 CFR 149.610",
"url": "https://www.law.cornell.edu/cfr/text/45/149.610",
"read_on": "10/19/2026"
},
"issued_on": "2026-10-19",
"issued_by": {
"provider_id": "p1",
"name": "Sample Family Medicine",
"role": "convening"
},
"uninsured_reason": {
"code": "not_filing_claim",
"words": "The patient has coverage but does not want a claim sent to it (45 CFR 149.610(a)(2)(xiii)(B))."
},
"elements": [
{
"cfr": "(c)(1)(i)",
"element": "patient",
"value": {
"first_name": "SAMPLE",
"last_name": "PATIENT",
"date_of_birth": "1980-02-14"
}
},
{
"cfr": "(c)(1)(ii)",
"element": "primary_service",
"value": {
"description": "A new patient visit and a sleep study",
"scheduled_date": "2026-11-09"
}
}
],
"expected_total": 530,
"expected_total_words": "Expected charges in all: $530.00.",
"timing": [
{
"rule": "45 CFR 149.610(b)(1)(vi)(B)",
"words": "Scheduled at least 10 business days ahead: issue it within 3 business days of scheduling."
}
],
"delivery": "In writing, on paper or electronically as the patient asks; an electronic copy must be one the patient can save and print, in plain language (45 CFR 149.610(e)(1)). Information given by phone does not replace the written estimate ((e)(2)).",
"retention": "Part of the patient’s medical record; copies of any estimate issued in the last 6 years are given on request (45 CFR 149.610(f)(1)).",
"not_laid_out": "A recurring course of care on one estimate (at most 12 months, 45 CFR 149.610(b)(1)(x)) and a co-provider’s own submission to the convening provider ((d)) are not laid out by this route.",
"notes": [
"Codes are checked for their shape only (an ICD-10-CM diagnosis, a CPT or HCPCS code, a DRG, an NDC); Payerworks does not look them up and prints no code descriptor."
],
"sources": [
{
"cite": "45 CFR 149.610",
"url": "https://www.law.cornell.edu/cfr/text/45/149.610",
"read_on": "10/19/2026"
},
{
"cite": "45 CFR 149.620",
"url": "https://www.law.cornell.edu/cfr/text/45/149.620",
"read_on": "10/19/2026"
}
],
"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 |
|---|---|---|
document | the value "good_faith_estimate" | |
provider_document | the value true | |
statement | string | |
rule | object | |
issued_on | string | |
issued_by | object | |
uninsured_reason | object | |
elements | list of object | |
expected_total | number | |
expected_total_words | string | |
timing | list of object | |
delivery | string | |
retention | string | |
not_laid_out | string | |
notes | list of string | |
sources | list of object | |
transmitted | the value false |
POST/v1/dme/replacement-window
When equipment may be replaced
For each item you send (a HCPCS code and the day it was delivered, at most 100): the earliest replacement for wear under Medicare’s reasonable useful lifetime, five years from delivery unless you name a longer lifetime and its source, whether a planned day falls inside it (judged from the latest delivery you sent of the same code), and what to consider by reason (an Advance Beneficiary Notice with GA or GZ, or the RA modifier for a loss, a theft or irreparable damage), each cited. It does not check Medicare’s records: only the DME MAC shows whether a same or similar item was supplied.
Request
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/dme/replacement-window" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.postDmeReplacementWindow(body);With the Python client
answer = pw.post_dme_replacement_window(body){
"items": [
{
"reference": "A",
"hcpcs": "E0601",
"delivered_on": "2021-03-15",
"planned_on": "2026-11-02"
},
{
"reference": "B",
"hcpcs": "K0001",
"delivered_on": "2023-05-02",
"planned_on": "2026-11-02",
"reason": "theft"
}
]
}{
"kind": "dme_replacement_window",
"checks_medicare_records": false,
"statement": "This does not check Medicare’s records. It works from the codes and delivery dates you sent; the patient may have had a same or similar item from another supplier, which only the DME MAC’s same or similar inquiry shows. Payerworks keeps not…",
"items": [
{
"reference": "A",
"hcpcs": "E0601",
"delivered_on": "2021-03-15",
"useful_lifetime_years": 5,
"useful_lifetime": {
"source": "cfr_floor",
"words": "5 years: the floor in 42 CFR 414.210(f)(1). A program instruction or the DME MAC may set a longer lifetime for this item, which this answer does not know."
},
"earliest_replacement_for_wear": "2026-03-15",
"earliest_words": "A replacement for wear is outside the lifetime on or after 03/15/2026 (5 years from delivery on 03/15/2021).",
"latest_delivery_for_code": true,
"planned_on": "2026-11-02",
"reason": null,
"inside_lifetime": false,
"consider": [
{
"step": "no_abn_for_lifetime",
"words": "The planned day is on or after the end of the lifetime, so the lifetime gives no reason for an ABN (Noridian JD DME, Same or Similar Denials (one DME MAC’s guidance)). A replacement for wear needs a new order, and a new CMN where one is req…",
"source": {
"cite": "Noridian JD DME, Same or Similar Denials (one DME MAC’s guidance)",
"url": "https://med.noridianmedicare.com/web/jddme/topics/same-or-similar/ss-denials",
"read_on": "10/19/2026"
}
}
],
"sources": [
{
"cite": "42 CFR 414.210(f)(1)",
"url": "https://www.law.cornell.edu/cfr/text/42/414.210",
"read_on": "10/19/2026"
},
{
"cite": "Medicare Benefit Policy Manual, Pub. 100-02, ch. 15, §110.2.C",
"url": "https://www.cms.gov/Regulations-and-Guidance/Guidance/Manuals/downloads/bp102c15.pdf",
"read_on": "10/19/2026"
}
]
},
{
"reference": "B",
"hcpcs": "K0001",
"delivered_on": "2023-05-02",
"useful_lifetime_years": 5,
"useful_lifetime": {
"source": "cfr_floor",
"words": "5 years: the floor in 42 CFR 414.210(f)(1). A program instruction or the DME MAC may set a longer lifetime for this item, which this answer does not know."
},
"earliest_replacement_for_wear": "2028-05-02",
"earliest_words": "A replacement for wear is outside the lifetime on or after 05/02/2028 (5 years from delivery on 05/02/2023).",
"latest_delivery_for_code": true,
"planned_on": "2026-11-02",
"reason": "theft",
"inside_lifetime": true,
"consider": [
{
"step": "ra_modifier",
"modifier": "RA",
"words": "A stolen item may be replaced inside the lifetime (42 CFR 414.210(f)(1)). Bill the replacement with RA, replacement of a DME item, and a narrative of why it was replaced (Medicare Claims Processing Manual, Pub. 100-04, ch. 20, §50.4); keep …",
"source": {
"cite": "Medicare Claims Processing Manual, Pub. 100-04, ch. 20, §50.4",
"url": "https://www.cms.gov/Regulations-and-Guidance/Guidance/Manuals/downloads/clm104c20.pdf",
"read_on": "10/19/2026"
}
}
],
"sources": [
{
"cite": "42 CFR 414.210(f)(1)",
"url": "https://www.law.cornell.edu/cfr/text/42/414.210",
"read_on": "10/19/2026"
},
{
"cite": "Medicare Benefit Policy Manual, Pub. 100-02, ch. 15, §110.2.C",
"url": "https://www.cms.gov/Regulations-and-Guidance/Guidance/Manuals/downloads/bp102c15.pdf",
"read_on": "10/19/2026"
}
]
}
],
"notes": [
"Replacing rented oxygen equipment, items needing frequent and substantial servicing, and rented inexpensive or routinely purchased items is not covered at all (Medicare Benefit Policy Manual, Pub. 100-02, ch. 15, §110.2.C); a HCPCS code alo…",
"Same or similar is judged across codes as well as within one; which codes Medicare treats as similar is the DME MAC’s, and this answer compares a code only with itself."
]
}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 | the value "dme_replacement_window" | |
checks_medicare_records | the value false | |
statement | string | |
items | list of object | |
notes | list of string |
POST/v1/financial-assistance/fpl
Income against the poverty guideline
Income as a percent of the 2026 HHS poverty guideline for the household’s size (Alaska and Hawaii on their own tables; a territory only when you say your program uses the contiguous table), and which of your policy’s tiers it falls in, compared in whole cents. Arithmetic only: no screening and no decision.
Request
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/financial-assistance/fpl" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.postFinancialAssistanceFpl(body);With the Python client
answer = pw.post_financial_assistance_fpl(body){
"household_size": 3,
"annual_income": 54640,
"state": "IL",
"tiers": [
{
"name": "Full discount",
"max_percent": 200
},
{
"name": "Half discount",
"max_percent": 300
}
]
}{
"kind": "fpl",
"household_size": 3,
"annual_income": 54640,
"income_input": {
"annual_income": 54640
},
"state": "IL",
"guideline": {
"year": 2026,
"table": "48 contiguous states and the District of Columbia",
"amount": 27320,
"source": {
"name": "2026 HHS Poverty Guidelines",
"publisher": "U.S. Department of Health and Human Services, Office of the Assistant Secretary for Planning and Evaluation (ASPE)",
"citation": "91 Fed. Reg. 1797 (01/15/2026), document 2026-00755",
"url": "https://aspe.hhs.gov/topics/poverty-economic-mobility/poverty-guidelines",
"federal_register_url": "https://www.govinfo.gov/content/pkg/FR-2026-01-15/html/2026-00755.htm",
"effective": "01/13/2026, unless a program sets another date",
"read_on": "10/19/2026"
}
},
"percent_of_guideline": 200,
"tier": {
"name": "Full discount",
"max_percent": 200
},
"tier_words": "At 200% of the guideline, inside your tier Full discount (up to 200%).",
"statement": "Arithmetic on the figures you sent against the published HHS poverty guideline. It is not a screening and not a decision: what counts as the household and its income, and what each tier grants, are the provider’s financial assistance policy…",
"screening": false,
"decision": 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 | the value "fpl" | |
household_size | integer | |
annual_income | number | |
income_input | object | |
state | string | |
guideline | object | |
percent_of_guideline | number | |
tier | object or null | |
tier_words | string | |
statement | string | |
screening | the value false | |
decision | the value false |
POST/v1/price-transparency/validate
Check a hospital standard charges file
A hospital’s standard charges file, as the tall or wide CSV or as JSON, checked against CMS’s version 3.0.0 template and schema: every required header and value, the valid values, and each conditional requirement, each problem with its row or path, the column and the rule it breaks. A file over 2 MB is checked in pieces, the first three rows sent again with each and row_offset set, so every row number is the file’s own. Nothing is hosted, published or kept.
Request
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/price-transparency/validate" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.postPriceTransparencyValidate(body);With the Python client
answer = pw.post_price_transparency_validate(body){
"csv": "hospital_name,last_updated_on,version,location_name,hospital_address,license_number|IL,type_2_npi,\"To the best of its knowledge and belief, this hospital has included all applicable standard charge information in accordance with the requirements of 45 CFR 180.50, and the information encoded is true, accurate, and complete as of the date in the file. This hospital has included all payer-specific negotiated charges in dollars that can be expressed as a dollar amount. For payer-specific negotiated charges that cannot be expressed as a dollar amount in the machine-readable file or not knowable in advance, the hospital attests that the payer-specific negotiated charge is based on a contractual algorithm, percentage or formula that precludes the provision of a dollar amount and has provided all necessary information available to the hospital for the public to be able to derive the dollar amount, including, but not limited to, the specific fee schedule or components referenced in such percentage, algorithm or formula.\",attester_name\nSample Community Hospital,2026-10-01,3.0.0,Sample Community Hospital,\"100 Main Street, Springfield, IL 62701\",SAMPLE-1,1234567893,true,Sample Attester\ndescription,code|1,code|1|type,modifiers,setting,drug_unit_of_measurement,drug_type_of_measurement,standard_charge|gross,standard_charge|discounted_cash,payer_name,plan_name,standard_charge|negotiated_dollar,standard_charge|negotiated_percentage,standard_charge|negotiated_algorithm,median_amount,10th_percentile,90th_percentile,count,standard_charge|methodology,standard_charge|min,standard_charge|max,additional_generic_notes\nOffice visit,510,RC,,outpatient,,,200,160,Sample Health Plan,PPO,120,,,,,,,fee schedule,110,120,\nHip replacement,470,MS-DRG,,inpatient,,,,,Sample Health Plan,PPO,,45,,,,,,percent of total billed charges,,,\n"
}{
"valid": false,
"format": "csv_tall",
"file_version": "3.0.0",
"errors": [
{
"row": 5,
"column": "count",
"code": "count_missing",
"rule": "csv_conditional_7",
"message": "Row 5 has a negotiated percentage or algorithm but no count of allowed amounts."
},
{
"row": 5,
"column": "median_amount",
"code": "allowed_amount_missing",
"rule": "csv_conditional_8",
"message": "Row 5 has a negotiated percentage or algorithm but no median allowed amount."
}
],
"warnings": [],
"error_count": 4,
"warning_count": 0,
"truncated": false,
"rows_checked": 2,
"template": {
"version": "3.0.0",
"repository": "https://github.com/CMSgov/hospital-price-transparency",
"commit": "5333564a710f80d7740180b9ffab8dbdcba9b502",
"committed_on": "06/09/2026",
"read_on": "10/19/2026",
"files": [
"documentation/CSV/README.md",
"documentation/CSV/templates/V3.0.0_Tall_CSV_Format_Template.csv"
],
"regulation": "45 CFR 180.50",
"terms": "A work of the United States federal government (CMS); the repository carries no license file."
},
"rules": {
"csv_conditional_1": "A negotiated charge (dollar, percentage or algorithm) needs its payer name, plan name and methodology; a payer or plan named in the tall layout needs a negotiated charge.",
"csv_conditional_2": "A row with a standard charge needs at least one code with its code type.",
"csv_conditional_3": "A code needs its code type in the matching column, and a code type its code.",
"csv_conditional_4": "A methodology of \"other\" needs an explanation in the notes for that charge.",
"csv_conditional_5": "An item or service needs at least one of: gross charge, discounted cash price, or a negotiated dollar, percentage or algorithm.",
"csv_conditional_6": "A negotiated dollar amount needs the de-identified minimum and maximum negotiated charges.",
"csv_conditional_7": "A negotiated percentage or algorithm needs the count of allowed amounts.",
"csv_conditional_8": "A negotiated percentage or algorithm needs the median, 10th and 90th percentile allowed amounts, unless the count is 0.",
"csv_conditional_9": "A count of 0 needs an explanation in the notes for that charge.",
"csv_conditional_10": "A code of type NDC needs the drug unit and drug type of measurement.",
"csv_conditional_11": "A modifier row with no code needs a description and one of: a negotiated dollar, percentage or algorithm, or notes.",
"csv_conditional_12": "A drug unit of measurement needs its drug type, and a drug type its unit.",
"json_conditional_1": "A methodology of \"other\" needs additional_payer_notes.",
"json_conditional_2": "A standard charge needs at least one of: gross_charge, discounted_cash, or a payer's negotiated dollar, percentage or algorithm.",
"json_conditional_3": "Each payers_information object needs a standard_charge_dollar, standard_charge_percentage or standard_charge_algorithm.",
"json_conditional_4": "A negotiated dollar amount needs minimum and maximum on its standard charge.",
"json_conditional_5": "A negotiated percentage or algorithm needs count.",
"json_conditional_6": "A negotiated percentage or algorithm needs median_amount, 10th_percentile and 90th_percentile, unless count is \"0\".",
"json_conditional_7": "A count of \"0\" needs additional_payer_notes.",
"json_conditional_8": "A code of type NDC needs drug_information.",
"schema": "The v3.0.0 JSON schema's own requirement.",
"template": "The v3.0.0 CSV template's own requirement."
},
"hosted": false,
"published": false,
"stored": 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 |
|---|---|---|
valid | boolean | |
format | one of "csv_tall", "csv_wide", "json" | |
file_version | string or null | |
errors | list of object | The first 1000. |
warnings | list of object | The first 200: our advice, never a fault. |
error_count | integer | |
warning_count | integer | |
truncated | boolean | |
rows_checked | integer | |
items_checked | integer | |
payer_plans | integer | |
template | object | CMS’s template, by its repository, commit and the day read. |
rules | object | Each rule named by an error, in Payerworks’ words. |
hosted | the value false | |
published | the value false | |
stored | the value false |
Reading the answer
- An estimate shows each line’s
patient_shareandplan_sharewith the deductible, copay and coinsurance applied, and thetotals, from the 271 you sent and the allowed amount you gave, with itsassumptionssaid. It is an estimate, not a promise of what the payer will pay.
Troubleshooting
member_required(422): The 271 answers more than one patient: send member_index to say which one.invalid_good_faith_estimate(422): An element the rule requires is missing; each is listed.guideline_year_not_held(422): Only the guideline year Payerworks holds is answered.
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 |
|---|---|---|
no_answer | 400 | Send the 271 as x12 (the file's text) or as json (the JSON answer). |
no_dme_items | 400 | Send items: each { hcpcs, delivered_on }, at most 100 a call. |
no_estimate_lines | 400 | Send lines: the planned services, each with the allowed amount you expect (allowed_amount), at most 50 a call. |
no_mrf | 400 | Send the file's text as csv (the tall or wide CSV template) or the file as json (an object, or its text). |
too_many_dme_items | 400 | At most 100 items a call. |
too_many_estimate_lines | 400 | At most 50 estimate lines a call. |
answer_too_large | 413 | This answer is larger than one response can carry: send less in each call (split the file, or send fewer claims or requests). |
file_too_large | 413 | This file holds more than one call can read: the error names what was counted (unit) and the most one call reads (limit). Split it and send each part in its own call. |
mrf_too_large | 413 | The file is longer than 1990000 characters, or holds more than 50000 rows or 50000 items: check it in pieces, the first three rows sent again with each piece and row_offset set. |
guideline_not_defined | 422 | The HHS poverty guidelines are not defined for Puerto Rico or the other territories; send use_contiguous_table: true if your program uses the contiguous table there. |
guideline_year_not_held | 422 | Payerworks holds the 2026 HHS poverty guidelines only; send guideline_year 2026 or leave it out. |
invalid_dme_items | 422 | One or more items cannot be read; nothing was answered. Each problem is listed. |
invalid_estimate | 422 | The estimate request has fields that cannot be read; nothing was figured. Each problem is listed. |
invalid_fpl_request | 422 | The request has fields that cannot be read; nothing was figured. Each problem is listed. |
invalid_good_faith_estimate | 422 | The Good Faith Estimate is missing elements 45 CFR 149.610 requires, or has fields that cannot be read; nothing was laid out. Each problem is listed. |
member_required | 422 | This 271 answers more than one patient: send member_index to say which one to estimate. |
mrf_not_json | 422 | json is not a JSON document that can be read. |
no_member | 422 | This 271 answers no patient to estimate. |
not_a_271 | 422 | This is not an X12 271 eligibility answer: send the 271 with its ISA envelope. |
not_a_271_json | 422 | This JSON is not an eligibility answer: it holds no plans, benefitsInformation, errors or subscriber. |
Example
patient-money.mjs runs every step below against the API with a test key. It needs payerworks.mjs and sample-data.mjs beside it.
// Payerworks example: a patient's share estimated from a 271. Docs: https://payerworks.com/docs/patient-money
//
// 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 patient-money.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 { ELIGIBILITY_QUESTION, copy, refusalOf } from './sample-data.mjs';
/** @param {Payerworks} pw */
export async function run(pw) {
// 1. A 271 to estimate from: the eligibility sandbox's answer.
const sandbox = await pw.postSandboxEligibility({ request: copy(ELIGIBILITY_QUESTION) });
// 2. The patient's share of two planned services, from that 271 and the allowed amounts you expect.
const estimate = await pw.postEstimates({
x12: sandbox.files.eligibility_271, network_level: 'in_network', date_of_service: '2026-10-20',
lines: [{ reference: 'visit', allowed_amount: 180 }, { reference: 'test', allowed_amount: 40 }],
});
// 3. The failure path: a line with no allowed amount is refused. Payerworks never supplies one.
const refused = await refusalOf(() => pw.postEstimates({ x12: sandbox.files.eligibility_271, network_level: 'in_network', date_of_service: '2026-10-20', lines: [{ reference: 'visit' }] }), { PayerworksError });
return { figured: estimate.figured, totals: estimate.totals, transmitted: estimate.transmitted, 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));
}
{
"figured": true,
"totals": {
"allowed_amount": 220,
"patient_share": 220,
"plan_share": 0,
"deductible_applied": 220,
"copay_applied": 0,
"coinsurance_amount": 0
},
"transmitted": false,
"refused": {
"status": 422,
"code": "invalid_estimate",
"request_id": "req_3"
}
}