Skip to content

Docs: by job

Patient estimates and money

What a patient may owe, figured from what you send, and a hospital’s standard charges file checked against the published template.

Read as markdown

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

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.json
request.jsonJSON
{
  "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
    }
  ]
}
The answer (200)JSON
{
  "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
FieldTypeWhat it is
kindthe value "estimate"
sourcestring
member_indexinteger
payerobject
planobject
date_of_servicestring or null
network_levelstring
coverage_levelstring
insurance_typestring or null
coverageobject
lineslist of object
totalsobject or nullNull when any line could not be figured.
figuredboolean
not_statedlist of object
assumptionslist of string
noteslist of string
eligibility_noteslist of string
refusalobjectWhen the 271 itself is a refusal: the explainer’s reasons.
transmittedthe 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.json
request.jsonJSON
{
  "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
  }
}
The answer (200)JSON
{
  "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
FieldTypeWhat it is
documentthe value "good_faith_estimate"
provider_documentthe value true
statementstring
ruleobject
issued_onstring
issued_byobject
uninsured_reasonobject
elementslist of object
expected_totalnumber
expected_total_wordsstring
timinglist of object
deliverystring
retentionstring
not_laid_outstring
noteslist of string
sourceslist of object
transmittedthe 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.json
request.jsonJSON
{
  "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"
    }
  ]
}
The answer (200)JSON
{
  "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
FieldTypeWhat it is
kindthe value "dme_replacement_window"
checks_medicare_recordsthe value false
statementstring
itemslist of object
noteslist 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.json
request.jsonJSON
{
  "household_size": 3,
  "annual_income": 54640,
  "state": "IL",
  "tiers": [
    {
      "name": "Full discount",
      "max_percent": 200
    },
    {
      "name": "Half discount",
      "max_percent": 300
    }
  ]
}
The answer (200)JSON
{
  "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
FieldTypeWhat it is
kindthe value "fpl"
household_sizeinteger
annual_incomenumber
income_inputobject
statestring
guidelineobject
percent_of_guidelinenumber
tierobject or null
tier_wordsstring
statementstring
screeningthe value false
decisionthe 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.json
request.jsonJSON
{
  "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"
}
The answer (200)JSON
{
  "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
FieldTypeWhat it is
validboolean
formatone of "csv_tall", "csv_wide", "json"
file_versionstring or null
errorslist of objectThe first 1000.
warningslist of objectThe first 200: our advice, never a fault.
error_countinteger
warning_countinteger
truncatedboolean
rows_checkedinteger
items_checkedinteger
payer_plansinteger
templateobjectCMS’s template, by its repository, commit and the day read.
rulesobjectEach rule named by an error, in Payerworks’ words.
hostedthe value false
publishedthe value false
storedthe value false

Reading the answer

  • An estimate shows each line’s patient_share and plan_share with the deductible, copay and coinsurance applied, and the totals, from the 271 you sent and the allowed amount you gave, with its assumptions said. 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
CodeStatusWhat it means
no_answer400Send the 271 as x12 (the file's text) or as json (the JSON answer).
no_dme_items400Send items: each { hcpcs, delivered_on }, at most 100 a call.
no_estimate_lines400Send lines: the planned services, each with the allowed amount you expect (allowed_amount), at most 50 a call.
no_mrf400Send 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_items400At most 100 items a call.
too_many_estimate_lines400At most 50 estimate lines a call.
answer_too_large413This answer is larger than one response can carry: send less in each call (split the file, or send fewer claims or requests).
file_too_large413This 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_large413The 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_defined422The 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_held422Payerworks holds the 2026 HHS poverty guidelines only; send guideline_year 2026 or leave it out.
invalid_dme_items422One or more items cannot be read; nothing was answered. Each problem is listed.
invalid_estimate422The estimate request has fields that cannot be read; nothing was figured. Each problem is listed.
invalid_fpl_request422The request has fields that cannot be read; nothing was figured. Each problem is listed.
invalid_good_faith_estimate422The 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_required422This 271 answers more than one patient: send member_index to say which one to estimate.
mrf_not_json422json is not a JSON document that can be read.
no_member422This 271 answers no patient to estimate.
not_a_271422This is not an X12 271 eligibility answer: send the 271 with its ISA envelope.
not_a_271_json422This 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.

patient-money.mjsJavaScript
// 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));
}
What it answered, run against the API in Payerworks’ own tests (your request ids will differ)JSON
{
  "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"
  }
}

Talk to us

What happens next

  1. 1

    A person at Payerworks reads what you sent, usually within a business day.

  2. 2

    One reply by email names the next step: our trading partner details, where each of your payers stands, or a key for the API.

  3. 3

    Nothing follows unless you answer. There is no sequence behind this form.

Or write to hello@payerworks.com.

Talk to Payerworks

Who you are and what you want to connect. A person replies by email, usually within a business day, and there is no follow-up sequence.

Used to reply to you and for nothing else.