# 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.

Page: https://payerworks.com/docs/patient-money · Markdown: https://payerworks.com/docs/patient-money.md

> 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/estimates](#post-v1-estimates): Estimate a patient’s share
- [POST /v1/estimates/good-faith](#post-v1-estimates-good-faith): Lay out a Good Faith Estimate
- [POST /v1/dme/replacement-window](#post-v1-dme-replacement-window): When equipment may be replaced
- [POST /v1/financial-assistance/fpl](#post-v1-financial-assistance-fpl): Income against the poverty guideline
- [POST /v1/price-transparency/validate](#post-v1-price-transparency-validate): Check 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

```bash
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/estimates" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d @request.json
```

With the JavaScript client

```js
const answer = await pw.postEstimates(body);
```

With the Python client

```python
answer = pw.post_estimates(body)
```

request.json

```json
{
  "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](https://payerworks.com/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

```bash
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
```

With the JavaScript client

```js
const answer = await pw.postEstimatesGoodFaith(body);
```

With the Python client

```python
answer = pw.post_estimates_good_faith(body)
```

request.json

```json
{
  "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](https://payerworks.com/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

```bash
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
```

With the JavaScript client

```js
const answer = await pw.postDmeReplacementWindow(body);
```

With the Python client

```python
answer = pw.post_dme_replacement_window(body)
```

request.json

```json
{
  "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](https://payerworks.com/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

```bash
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
```

With the JavaScript client

```js
const answer = await pw.postFinancialAssistanceFpl(body);
```

With the Python client

```python
answer = pw.post_financial_assistance_fpl(body)
```

request.json

```json
{
  "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](https://payerworks.com/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

```bash
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
```

With the JavaScript client

```js
const answer = await pw.postPriceTransparencyValidate(body);
```

With the Python client

```python
answer = pw.post_price_transparency_validate(body)
```

request.json

```json
{
  "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](https://payerworks.com/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_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](https://payerworks.com/docs/errors).

| 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](https://payerworks.com/docs/examples/patient-money.mjs) runs every step below against the API with a test key. It needs [payerworks.mjs](https://payerworks.com/sdk/payerworks.mjs) and [sample-data.mjs](https://payerworks.com/docs/examples/sample-data.mjs) beside it.

patient-money.mjs

```js
// 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"
  }
}
```
