# Analytics from your own files

> Figures from the files you send, each with how many it rests on. Payerworks has no data of anyone else’s to compare you with.

Page: https://payerworks.com/docs/analytics · Markdown: https://payerworks.com/docs/analytics.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.

Four routes count what your own files show: your denials and payments, payments against the fee schedule you send, a claim against your own past denials, and your open denials ranked. Each reads only what you send in the call, and nothing is stored.

## Routes

- [POST /v1/analytics/summary](#post-v1-analytics-summary): Summarize your claims
- [POST /v1/remittances/variance](#post-v1-remittances-variance): Check payments against your fee schedule
- [POST /v1/claims/risk](#post-v1-claims-risk): Check a claim against your past denials
- [POST /v1/denials/prioritize](#post-v1-denials-prioritize): Rank your open denials

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/analytics/summary

**Summarize your claims.** Send the claims with the 999s, 277CAs and 835s you hold for them (the same body as /v1/claims/track), or your 835s alone. You get first-pass acceptance, the rejection reasons ranked, the denial rate by payer and by group code, the dollars refused, the days from sending a claim to its remittance by payer (the median and quartiles), reversals, and what the refusals come down to by reason. Every figure says how many it counts, and a rate or a median is read as a pattern only from five; below that the answer says there is not enough history. Send month (YYYY-MM) for one calendar month: the current month counts to today. It reads only your files: there is no benchmark, and nothing is stored.

Request

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

With the JavaScript client

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

With the Python client

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

request.json

```json
{
  "x12_837": "ISA*00*          *00*          *ZZ*SAMPLEBILLER   *ZZ*PWSANDBOX      *261019*1500*^*00501*000000001*0*T*:~GS*HC*SAMPLEBILLER*PWSANDBOX*20261019*1500*1*X*005010X222A1~ST*837*0001*005010X222A1~BHT*0019*00*PW-SBX-PAID*20261019*1500*CH~NM1*41*2*Sample Sleep Dentistry*****46*SAMPLEBILLER~PER*IC*Billing Office*TE*5555550100~NM1*40*2*Sample Health Plan*****46*PWSANDBOX~HL*1**20*1~PRV*BI*PXC*1223S0112X~NM1*85*2*Sample Sleep Dentistry*****XX*1234567893~N3*100 Main Street~N4*Springfield*IL*627011234~REF*EI*123456789~HL*2*1*22*0~SBR*P*18*******CI~NM1*IL*1*Patient*Sample****MI*W123456789~N3*9 Elm Street~N4*Springfield*IL*62701~DMG*D8*19600214*F~NM1*PR*2*Sample Health Plan*****PI*99999~CLM*PW-SBX-PAID*2900***11:B:1*Y*A*Y*Y~HI*ABK:G4733~LX*1~SV1*HC:E0486:KX*2900*UN*1***1~DTP*472*D8*20261001~SE*24*0001~ST*837*0002*005010X222A1~BHT*0019*00*PW-SBX-DENY*20261019*1500*CH~NM1*41*2*Sample Sleep Dentistry*****46*SAMPLEBILLER~PER*IC*Billing Office*TE*5555550100~NM1*40*2*Sample Health Plan*****46*PWSANDBOX~HL*1**20*1~PRV*BI*PXC*1223S0112X~NM1*85*2*Sample Sleep Dentistry*****XX*1234567893~N3*100 Main Street~N4*Springfield*IL*627011234~REF*EI*123456789~HL*2*1*22*0~SBR*P*18*******CI~NM1*IL*1*Patient*Sample****MI*W12345DENY~N3*9 Elm Street~N4*Springfield*IL*62701~DMG*D8*19600214*F~NM1*PR*2*Sample Health Plan*****PI*99999~CLM*PW-SBX-DENY*2900***11:B:1*Y*A*Y*Y~HI*ABK:G4733~LX*1~SV1*HC:E0486:KX*2900*UN*1***1~DTP*472*D8*20261001~SE*24*0002~ST*837*0003*005010X222A1~BHT*0019*00*PW-SBX-REJ*20261019*1500*CH~NM1*41*2*Sample Sleep Dentistry*****46*SAMPLEBILLER~PER*IC*Billing Office*TE*5555550100~NM1*40*2*Sample Health Plan*****46*PWSANDBOX~HL*1**20*1~PRV*BI*PXC*1223S0112X~NM1*85*2*Sample Sleep Dentistry*****XX*1234567893~N3*100 Main Street~N4*Springfield*IL*627011234~REF*EI*123456789~HL*2*1*22*0~SBR*P*18*******CI~NM1*IL*1*Patient*Sample****MI*W12345REJ~N3*9 Elm Street~N4*Springfield*IL*62701~DMG*D8*19600214*F~NM1*PR*2*Sample Health Plan*****PI*99999~CLM*PW-SBX-REJ*2900***11:B:1*Y*A*Y*Y~HI*ABK:G4733~LX*1~SV1*HC:E0486:KX*2900*UN*1***1~DTP*472*D8*20261001~SE*24*0003~ST*837*0004*005010X222A1~BHT*0019*00*PW-SBX-D16*20261019*1500*CH~NM1*41*2*Sample Sleep Dentistry*****46*SAMPLEBILLER~PER*IC*Billing Office*TE*5555550100~NM1*40*2*Sample Health Plan*****46*PWSANDBOX~HL*1**20*1~PRV*BI*PXC*1223S0112X~NM1*85*2*Sample Sleep Dentistry*****XX*1234567893~N3*100 Main Street~N4*Springfield*IL*627011234~REF*EI*123456789~HL*2*1*22*0~SBR*P*18*******CI~NM1*IL*1*Patient*Sample****MI*W12345D16~N3*9 Elm Street~N4*Springfield*IL*62701~DMG*D8*19600214*F~NM1*PR*2*Sample Health Plan*****PI*99999~CLM*PW-SBX-D16*2900***11:B:1*Y*A*Y*Y~HI*ABK:G4733~LX*1~SV1*HC:E0486:KX*2900*UN*1***1~DTP*472*D8*20261001~SE*24*0004~ST*837*0005*005010X222A1~BHT*0019*00*PW-SBX-PEND*20261019*1500*CH~NM1*41*2*Sample Sleep Dentistry*****46*SAMPLEBILLER~PER*IC*Billing Office*TE*5555550100~NM1*40*2*Sample Health Plan*****46*PWSANDBOX~HL*1**20*1~PRV*BI*PXC*1223S0112X~NM1*85*2*Sample Sleep Dentistry*****XX*1234567893~N3*100 Main Street~N4*Springfield*IL*627011234~REF*EI*123456789~HL*2*1*22*0~SBR*P*18*******CI~NM1*IL*1*Patient*Sample****MI*W12345PEND~N3*9 Elm Street~N4*Springfield*IL*62701~DMG*D8*19600214*F~NM1*PR*2*Sample Health Plan*****PI*99999~CLM*PW-SBX-PEND*2900***11:B:1*Y*A*Y*Y~HI*ABK:G4733~LX*1~SV1*HC:E0486:KX*2900*UN*1***1~DTP*472*D8*20261001~SE*24*0005~ST*837*0006*005010X222A1~BHT*0019*00*PW-SBX-DED*20261019*1500*CH~NM1*41*2*Sample Sleep Dentistry*****46*SAMPLEBILLER~PER*IC*Billing Office*TE*5555550100~NM1*40*2*Sample Health Plan*****46*PWSANDBOX~HL*1**20*1~PRV*BI*PXC*1223S0112X~NM1*85*2*Sample Sleep Dentistry*****XX*1234567893~N3*100 Main Street~N4*Springfield*IL*627011234~REF*EI*123456789~HL*2*1*22*0~SBR*P*18*******CI~NM1*IL*1*Patient*Sample****MI*W12345DED~N3*9 Elm Street~N4*Springfield*IL*62701~DMG*D8*19600214*F~NM1*PR*2*Sample Health Plan*****PI*99999~CLM*PW-SBX-DED*2900***11:B:1*Y*A*Y*Y~HI*ABK:G4733~LX*1~SV1*HC:E0486:KX*2900*UN*1***1~DTP*472*D8*20261001~SE*24*0006~GE*6*1~IEA*1*000000001~",
  "acknowledgments": [
    "ISA*00*          *00*          *ZZ*PWSANDBOX      *ZZ*SAMPLEBILLER   *261019*1500*^*00501*000000001*0*T*:~GS*FA*PWSANDBOX*SAMPLEBILLER*20261019*1500*1*X*005010X231A1~ST*999*0001*005010X231A1~AK1*HC*1*005010X222A1~AK2*837*0001*005010X222A1~IK5*A~AK2*837*0002*005010X222A1~IK5*A~AK2*837*0003*005010X222A1~IK5*A~AK2*837*0004*005010X222A1~IK5*A~AK2*837*0005*005010X222A1~IK5*A~AK2*837*0006*005010X222A1~IK5*A~AK9*A*6*6*6~SE*16*0001~GE*1*1~IEA*1*000000001~",
    "ISA*00*          *00*          *ZZ*PWSANDBOX      *ZZ*SAMPLEBILLER   *261019*1500*^*00501*000000002*0*T*:~GS*HN*PWSANDBOX*SAMPLEBILLER*20261019*1500*2*X*005010X214~ST*277*0001*005010X214~BHT*0085*08*PWSANDBOX2*20261019*1500*TH~HL*1**20*1~NM1*PR*2*PAYERWORKS SANDBOX PAYER*****PI*PWSANDBOX~TRN*1*PWSANDBOX2~DTP*050*D8*20261019~DTP*009*D8*20261019~HL*2*1*21*1~NM1*41*2*Sample Sleep Dentistry*****46*SAMPLEBILLER~TRN*2*000000001~STC*A1:19:PR*20261019*WQ*17400~QTY*90*5~QTY*AA*1~AMT*YU*14500~AMT*YY*2900~HL*3*2*19*1~NM1*85*2*Sample Sleep Dentistry*****XX*1234567893~TRN*1*0~HL*4*3*PT~NM1*QC*1*Patient*Sample****MI*W123456789~TRN*2*PW-SBX-PAID~STC*A2:20*20261019*WQ*2900~REF*1K*PWS2292711347~DTP*472*D8*20261001~HL*5*3*PT~NM1*QC*1*Patient*Sample****MI*W12345DENY~TRN*2*PW-SBX-DENY~STC*A2:20*20261019*WQ*2900~REF*1K*PWS0752053091~DTP*472*D8*20261001~HL*6*3*PT~NM1*QC*1*Patient*Sample****MI*W12345REJ~TRN*2*PW-SBX-REJ~STC*A7:164:IL*20261019*U*2900~DTP*472*D8*20261001~HL*7*3*PT~NM1*QC*1*Patient*Sample****MI*W12345D16~TRN*2*PW-SBX-D16~STC*A2:20*20261019*WQ*2900~REF*1K*PWS1120978316~DTP*472*D8*20261001~HL*8*3*PT~NM1*QC*1*Patient*Sample****MI*W12345PEND~TRN*2*PW-SBX-PEND~STC*A2:20*20261019*WQ*2900~REF*1K*PWS0688343368~DTP*472*D8*20261001~HL*9*3*PT~NM1*QC*1*Patient*Sample****MI*W12345DED~TRN*2*PW-SBX-DED~STC*A2:20*20261019*WQ*2900~REF*1K*PWS0903628598~DTP*472*D8*20261001~SE*54*0001~GE*1*2~IEA*1*000000002~"
  ],
  "remittances": [
    "ISA*00*          *00*          *ZZ*PWSANDBOX      *ZZ*SAMPLEBILLER   *261019*1500*^*00501*000000003*0*T*:~GS*HP*PWSANDBOX*SAMPLEBILLER*20261019*1500*3*X*005010X221A1~ST*835*0001~BPR*I*1740*C*CHK************20261019~TRN*1*PWSANDBOX-000000003-1*1PWSANDBOX~DTM*405*20261019~N1*PR*PAYERWORKS SANDBOX PAYER*XV*PWSANDBOX~REF*2U*PWSANDBOX~N1*PE*Sample Sleep Dentistry*XX*1234567893~N3*100 Main Street~N4*Springfield*IL*627011234~REF*TJ*123456789~LX*1~CLP*PW-SBX-PAID*1*2900*1392*348*CI*PWS2292711347*11*1~NM1*QC*1*Patient*Sample****MI*W123456789~SVC*HC:E0486:KX*2900*1392**1~DTM*472*20261001~CAS*CO*45*1160~CAS*PR*2*348~AMT*B6*1740~CLP*PW-SBX-DENY*1*2900*0*0*CI*PWS0752053091*11*1~NM1*QC*1*Patient*Sample****MI*W12345DENY~SVC*HC:E0486:KX*2900*0**1~DTM*472*20261001~CAS*CO*50*2900~AMT*B6*0~LQ*HE*N115~CLP*PW-SBX-D16*1*2900*0*0*CI*PWS1120978316*11*1~NM1*QC*1*Patient*Sample****MI*W12345D16~SVC*HC:E0486:KX*2900*0**1~DTM*472*20261001~CAS*CO*16*2900~AMT*B6*0~LQ*HE*M51~CLP*PW-SBX-DED*1*2900*348*1392*CI*PWS0903628598*11*1~NM1*QC*1*Patient*Sample****MI*W12345DED~SVC*HC:E0486:KX*2900*348**1~DTM*472*20261001~CAS*CO*45*1160~CAS*PR*1*1392~AMT*B6*1740~SE*40*0001~GE*1*3~IEA*1*000000003~"
  ],
  "month": "2026-10"
}
```

The answer (200)

```json
{
  "unit": "claims",
  "period": {
    "month": "2026-10",
    "from": "2026-10-01",
    "through": "2026-10-19",
    "month_to_date": true,
    "basis": "Each claim is counted in the month of the day it was first sent (its 837's date), else its date of service; a claim payment, in the month of its date of service.",
    "words": "The calendar month to date, through 10/19/2026."
  },
  "claims": {
    "n": 6,
    "sent": 6,
    "outside_the_month": 0,
    "undated_left_out": 0,
    "states": {
      "processed": 2,
      "denied": 2,
      "rejected": 1,
      "accepted": 1
    }
  },
  "first_pass": {
    "n": 6,
    "accepted": 5,
    "rejected": 1,
    "received_only": 0,
    "not_acknowledged": 0,
    "acceptance_rate": 0.833,
    "enough_history": true,
    "words": "Of the 6 claims whose first submission's first acknowledgment accepted or rejected it, 5 were accepted. 0 were only received at the first acknowledgment, and 0 have no acknowledgment of their first submission among the files sent: neither i…"
  },
  "rejections": {
    "claims_rejected": 1,
    "reasons": [
      {
        "kind": "277CA",
        "category": "A7",
        "code": "164",
        "entity": "IL",
        "claims": 1,
        "words": "The claim was rejected for invalid information: the subscriber's contract or member number is missing or invalid (STC A7:164:IL). Fix it on the subscriber in your claim data, then send the claim again."
      }
    ],
    "words": "Each reason counts the claims it rejected at least once, on any submission; a claim rejected for two reasons is counted under both."
  },
  "denials": {
    "unit": "claims",
    "decided": 4,
    "denied": 2,
    "denial_rate": 0.5,
    "enough_history": false,
    "words": "Not enough history: 4 are fewer than the 5 this figure is read as a pattern from.",
    "partly_refused": 0,
    "dollars_refused": 5800,
    "dollars_refused_cents": 580000,
    "by_payer": [
      {
        "payer_id": "99999",
        "payer_id_source": "claim",
        "decided": 4,
        "denied": 2,
        "denial_rate": 0.5,
        "dollars_refused": 5800,
        "enough_history": false,
        "words": "Not enough history: 4 are fewer than the 5 this figure is read as a pattern from."
      }
    ],
    "by_group": {
      "n": 2,
      "words": "Of the 2 claims with money refused, how many had a refusing adjustment under each group code (one may have several).",
      "groups": [
        {
          "group": "CO",
          "name": "Contractual obligation",
          "known": true,
          "count": 2,
          "share": 1,
          "dollars_refused": 5800
        }
      ]
    },
    "reasons": [
      {
        "group": "CO",
        "code": "16",
        "label": "Claim lacks information or has a submission/billing error",
        "known": true,
        "label_source": "paraphrase",
        "root_cause": "missing_information",
        "count": 1,
        "share": 0.5,
        "dollars_refused": 2900
      },
      {
        "group": "CO",
        "code": "50",
        "label": "Non-covered: not deemed a medical necessity",
        "known": true,
        "label_source": "paraphrase",
        "root_cause": "medical_necessity",
        "count": 1,
        "share": 0.5,
        "dollars_refused": 2900
      }
    ],
    "root_cause": {
      "n": 2,
      "words": "Each of the 2 claims with money refused is placed by the reason that refused the most money on it; the dollars count every refusing adjustment by its own reason.",
      "categories": [
        {
          "category": "medical_necessity",
          "words": "The payer judged the service not medically necessary, unproven or experimental, or the records not to support its level, frequency or diagnosis.",
          "count": 1,
          "share": 0.5,
          "dollars_refused": 2900
        },
        {
          "category": "missing_information",
          "words": "Information or an attachment the payer needed was missing, wrong or not documented.",
          "count": 1,
          "share": 0.5,
          "dollars_refused": 2900
        }
      ],
      "source": {
        "list": "X12 claim adjustment reason codes",
        "url": "https://x12.org/codes/claim-adjustment-reason-codes",
        "read_on": "10/19/2026",
        "grouping": "Payerworks' own grouping of each code by its published words; the codes' words are X12's and are not copied here."
      }
    }
  },
  "days_to_remittance": {
    "words": "Days from the day the claim's latest submission was sent (its 837's date) to the first remittance that decided it. A claim with no submission date is not counted.",
    "all_payers": {
      "n": 4,
      "median": 0,
      "p25": 0,
      "p75": 0,
      "min": 0,
      "max": 0,
      "enough_history": false
    },
    "by_payer": [
      {
        "payer_id": "99999",
        "payer_id_source": "claim",
        "n": 4,
        "median": 0,
        "p25": 0,
        "p75": 0,
        "min": 0,
        "max": 0,
        "enough_history": false,
        "words": "Not enough history: 4 are fewer than the 5 this figure is read as a pattern from."
      }
    ],
    "not_counted": 0
  },
  "reversals": {
    "claims_with_reversals": 0,
    "reversals": 0,
    "taken_back": 0,
    "taken_back_of_payments_not_sent": 0,
    "words": "A reversal takes back an earlier payment. Money taken back from a payment among the files sent is counted in taken_back; from a payment not among them, apart."
  },
  "files_not_tied": {
    "items": 0,
    "words": "Every acknowledgment and remittance item was tied to a claim sent."
  },
  "floor": {
    "min_n": 5,
    "words": "A rate or a median is read as a pattern only from 5. Every figure counts only the files sent: Payerworks holds no other history, and no figure here is a benchmark."
  },
  "reads": "Only the files sent in this call: nothing is stored, and no payer is asked."
}
```

Shortened here: lists show their first two items and long strings are cut. The whole answer is this route’s example in [/openapi.json](https://payerworks.com/openapi.json).

| Field | Type | What it is |
| --- | --- | --- |
| `unit` | one of "claims", "claim payments" |  |
| `period` | object | The month counted, or every claim in the files. |
| `claims` | object |  |
| `claim_payments` | object |  |
| `first_pass` | object | First-pass acceptance: n, the rate (null below the floor’s count), and what was left out of it. |
| `rejections` | object |  |
| `denials` | object | The denial rate with its n, by payer, by group code and by reason, and the dollars refused. |
| `days_to_remittance` | object | Days from sending a claim to its remittance, by payer: n, the median and the quartiles. |
| `reversals` | object |  |
| `floor` | object |  |
| `today` | string |  |
| `reads` | string |  |

### POST /v1/remittances/variance

**Check payments against your fee schedule.** Send your 835s and your fee schedule (payer_id, code, modifier, allowed, effective_from, effective_to). Each service line is judged against the rate in force on its date of service: the amount expected, the payer’s allowed amount, the difference, and the adjustment codes the payer gave. A line with no rate says so and is never read as zero, and a line the payer refused is a denial, not an underpayment. Add medicare (a state) to compare with Medicare’s October 2026 DMEPOS fee schedule where the API runs, labeled as Medicare’s rate and never as your contract. An underpaid line carries the facts for POST /v1/appeals/letter.

Request

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

With the JavaScript client

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

With the Python client

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

request.json

```json
{
  "remittances": [
    "ISA*00*          *00*          *ZZ*SAMPLEPAYER    *ZZ*SAMPLEBILLER   *261018*0900*^*00501*000000093*0*T*:~\nGS*HP*SAMPLEPAYER*SAMPLEBILLER*20261018*0900*93*X*005010X221A1~\nST*835*0001~\nBPR*I*80*C*ACH*CCP*01*999999999*DA*123456*1234567890**01*999999999*DA*654321*20261018~\nTRN*1*EFT-20261018-02*1234567890~\nN1*PR*SAMPLE HEALTH PLAN*PI*99999~\nN1*PE*SAMPLE FAMILY MEDICINE*XX*1234567893~\nLX*1~\nCLP*PW-2003*1*215*0**12*PAYER-CN-23*11~\nNM1*QC*1*PATIENT*SAMPLE****MI*W123456789~\nDTM*232*20261001~\nSVC*HC:99213*150*0~\nDTM*472*20261001~\nCAS*CO*16*150~\nLQ*HE*N290~\nSVC*HC:87880*65*0~\nDTM*472*20261001~\nCAS*CO*16*65~\nLQ*HE*N290~\nCLP*PW-2004*1*350*0**MB*PAYER-CN-24*11~\nNM1*QC*1*PATIENT*SECOND****MI*1EG4TE5MK73~\nDTM*232*20261002~\nSVC*HC:99214*350*0~\nDTM*472*20261002~\nCAS*CO*50*350~\nLQ*HE*N115~\nCLP*PW-2005*1*240*80*100*12*PAYER-CN-25*11~\nNM1*QC*1*PATIENT*THIRD****MI*W555555555~\nDTM*232*20261003~\nSVC*HC:99213*240*80~\nDTM*472*20261003~\nCAS*CO*45*60~\nCAS*PR*1*100~\nAMT*B6*180~\nCLP*PW-2006*1*150*0**12*PAYER-CN-26*11~\nNM1*QC*1*PATIENT*FOURTH****MI*W666666666~\nDTM*232*20260101~\nSVC*HC:99213*150*0~\nDTM*472*20260101~\nCAS*CO*29*150~\nSE*39*0001~\nGE*1*93~\nIEA*1*000000093~"
  ],
  "fee_schedule": [
    {
      "payer_id": "99999",
      "code": "99213",
      "allowed": 210,
      "effective_from": "2026-01-01"
    },
    {
      "payer_id": "99999",
      "code": "99214",
      "allowed": 260,
      "effective_from": "2026-01-01"
    }
  ]
}
```

The answer (200)

```json
{
  "lines": [
    {
      "source": "remittances[0]",
      "claim_index": 0,
      "reference": "PW-2003",
      "payer_claim_number": "PAYER-CN-23",
      "payer_id": "99999",
      "line": 0,
      "procedure_code": "99213",
      "modifiers": [],
      "service_date": "2026-10-01",
      "units_paid": null,
      "billed": 150,
      "paid": 0,
      "allowed": null,
      "expected_allowed": null,
      "variance": null,
      "status": "refused",
      "words": "The payer refused money on this line: it is a denial, not a pricing question. POST /v1/denials/next-step says what to do.",
      "rate": null,
      "adjustments": [
        {
          "group": "CO",
          "code": "16",
          "label": "Claim lacks information or has a submission/billing error",
          "amount": 150
        }
      ]
    },
    {
      "source": "remittances[0]",
      "claim_index": 0,
      "reference": "PW-2003",
      "payer_claim_number": "PAYER-CN-23",
      "payer_id": "99999",
      "line": 1,
      "procedure_code": "87880",
      "modifiers": [],
      "service_date": "2026-10-01",
      "units_paid": null,
      "billed": 65,
      "paid": 0,
      "allowed": null,
      "expected_allowed": null,
      "variance": null,
      "status": "refused",
      "words": "The payer refused money on this line: it is a denial, not a pricing question. POST /v1/denials/next-step says what to do.",
      "rate": null,
      "adjustments": [
        {
          "group": "CO",
          "code": "16",
          "label": "Claim lacks information or has a submission/billing error",
          "amount": 65
        }
      ]
    }
  ],
  "totals": {
    "lines": 5,
    "judged": 1,
    "as_expected": 0,
    "underpaid": 1,
    "underpaid_dollars": 30,
    "above_expected": 0,
    "above_expected_dollars": 0,
    "no_rate": 0,
    "refused": 4,
    "no_allowed_stated": 0,
    "reversals_and_predeterminations_left_out": 0,
    "repeats_left_out": 0,
    "words": "1 of 5 lines were judged against your fee schedule; 1 was allowed below your rate, by $30.00 in all. A line with no rate is not judged and is never read as zero."
  },
  "by_payer": [
    {
      "payer_id": "99999",
      "payer_id_source": "N1*PR",
      "lines": 5,
      "judged": 1,
      "underpaid": 1,
      "underpaid_dollars": 30,
      "above_expected": 0,
      "above_expected_dollars": 0,
      "no_rate": 0
    }
  ],
  "rule": "Each line is judged by the row in force on its date of service: a row with effective_from applies from that day, effective_to ends it, and of several rows the newest in force wins. A row naming a modifier the line carries is chosen over the…",
  "letters": "An underpaid line carries the facts POST /v1/appeals/letter (kind underpayment) can be sent, from the remittance and your own rate; add the patient, member ID, provider and signer.",
  "reads": "Only the files and fee schedule sent in this call: nothing is stored, and no payer is asked."
}
```

Shortened here: lists show their first two items and long strings are cut. The whole answer is this route’s example in [/openapi.json](https://payerworks.com/openapi.json).

| Field | Type | What it is |
| --- | --- | --- |
| `lines` | list of object |  |
| `totals` | object |  |
| `by_payer` | list of object |  |
| `medicare` | object |  |
| `rule` | string |  |
| `letters` | string |  |
| `reads` | string |  |

### POST /v1/claims/risk

**Check a claim against your past denials.** Send a claim (as /v1/claims/validate takes it) and your past 835s. For each line, every past refusal of the same payer, procedure code and modifiers, with its reason and remark codes, on how many claims, out of how many decided claims with a line of that shape, and the fix. A reason is called a pattern only from two refused claims: a claim counts once however many of its lines were refused, and a remittance sent twice counts once. Send the past claims too and the diagnosis is compared. It reads only your files and never says how a payer will decide.

Request

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

With the JavaScript client

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

With the Python client

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

request.json

```json
{
  "claim": {
    "tradingPartnerServiceId": "99999",
    "submitter": {
      "organizationName": "Sample Family Medicine",
      "contactInformation": {
        "name": "Billing Office",
        "phoneNumber": "5555550100"
      }
    },
    "receiver": {
      "organizationName": "Sample Health Plan"
    },
    "billing": {
      "npi": "1234567893",
      "employerId": "123456789",
      "organizationName": "Sample Family Medicine",
      "taxonomyCode": "207Q00000X",
      "address": {
        "address1": "100 Main Street",
        "city": "Springfield",
        "state": "IL",
        "postalCode": "627011234"
      }
    },
    "subscriber": {
      "memberId": "W123456789",
      "paymentResponsibilityLevelCode": "P",
      "firstName": "Sample",
      "lastName": "Patient",
      "gender": "F",
      "dateOfBirth": "19800214",
      "address": {
        "address1": "9 Elm Street",
        "city": "Springfield",
        "state": "IL",
        "postalCode": "62701"
      }
    },
    "claimInformation": {
      "claimFilingCode": "CI",
      "patientControlNumber": "PW-2001",
      "claimChargeAmount": "215.00",
      "placeOfServiceCode": "11",
      "claimFrequencyCode": "1",
      "signatureIndicator": "Y",
      "planParticipationCode": "A",
      "benefitsAssignmentCertificationIndicator": "Y",
      "releaseInformationCode": "Y",
      "healthCareCodeInformation": [
        {
          "diagnosisTypeCode": "ABK",
          "diagnosisCode": "J029"
        },
        {
          "diagnosisTypeCode": "ABF",
          "diagnosisCode": "R509"
        }
      ],
      "serviceLines": [
        {
          "serviceDate": "20261001",
          "professionalService": {
            "procedureIdentifier": "HC",
            "procedureCode": "99213",
            "lineItemChargeAmount": "150.00",
            "measurementUnit": "UN",
            "serviceUnitCount": "1",
            "compositeDiagnosisCodePointers": {
              "diagnosisCodePointers": [
                "1",
                "2"
              ]
            }
          }
        },
        {
          "serviceDate": "20261001",
          "professionalService": {
            "procedureIdentifier": "HC",
            "procedureCode": "87880",
            "lineItemChargeAmount": "65.00",
            "measurementUnit": "UN",
            "serviceUnitCount": "1",
            "compositeDiagnosisCodePointers": {
              "diagnosisCodePointers": [
                "1"
              ]
            }
          }
        }
      ]
    }
  },
  "remittances": [
    "ISA*00*          *00*          *ZZ*SAMPLEPAYER    *ZZ*SAMPLEBILLER   *261018*0900*^*00501*000000093*0*T*:~\nGS*HP*SAMPLEPAYER*SAMPLEBILLER*20261018*0900*93*X*005010X221A1~\nST*835*0001~\nBPR*I*80*C*ACH*CCP*01*999999999*DA*123456*1234567890**01*999999999*DA*654321*20261018~\nTRN*1*EFT-20261018-02*1234567890~\nN1*PR*SAMPLE HEALTH PLAN*PI*99999~\nN1*PE*SAMPLE FAMILY MEDICINE*XX*1234567893~\nLX*1~\nCLP*PW-2003*1*215*0**12*PAYER-CN-23*11~\nNM1*QC*1*PATIENT*SAMPLE****MI*W123456789~\nDTM*232*20261001~\nSVC*HC:99213*150*0~\nDTM*472*20261001~\nCAS*CO*16*150~\nLQ*HE*N290~\nSVC*HC:87880*65*0~\nDTM*472*20261001~\nCAS*CO*16*65~\nLQ*HE*N290~\nCLP*PW-2004*1*350*0**MB*PAYER-CN-24*11~\nNM1*QC*1*PATIENT*SECOND****MI*1EG4TE5MK73~\nDTM*232*20261002~\nSVC*HC:99214*350*0~\nDTM*472*20261002~\nCAS*CO*50*350~\nLQ*HE*N115~\nCLP*PW-2005*1*240*80*100*12*PAYER-CN-25*11~\nNM1*QC*1*PATIENT*THIRD****MI*W555555555~\nDTM*232*20261003~\nSVC*HC:99213*240*80~\nDTM*472*20261003~\nCAS*CO*45*60~\nCAS*PR*1*100~\nAMT*B6*180~\nCLP*PW-2006*1*150*0**12*PAYER-CN-26*11~\nNM1*QC*1*PATIENT*FOURTH****MI*W666666666~\nDTM*232*20260101~\nSVC*HC:99213*150*0~\nDTM*472*20260101~\nCAS*CO*29*150~\nSE*39*0001~\nGE*1*93~\nIEA*1*000000093~"
  ]
}
```

The answer (200)

```json
{
  "reference": "PW-2001",
  "payer_ids_compared": [
    "99999"
  ],
  "history": {
    "claim_payments_read": 4,
    "decisions_read": 4,
    "for_this_payer": 4,
    "not_decisions": 0,
    "repeats_set_aside": 0,
    "payer_ids_in_files": [
      {
        "payer_id": "99999",
        "claim_payments": 4
      }
    ],
    "diagnosis": "No past claims were sent, so diagnoses are not compared: an 835 does not carry them. Send past_claims to compare them too."
  },
  "lines": [
    {
      "index": 0,
      "procedure_code": "99213",
      "modifiers": [],
      "diagnoses": [
        "J029",
        "R509"
      ],
      "verdict": "refused_before",
      "words": "Refused before, not enough history for a pattern: this payer refused 99213 with no modifier on 2 of 3 decided claims with a line of this shape, and no reason came up on 2 claims or more.",
      "same_shape": {
        "refused": 2,
        "paid": 1,
        "other": 0,
        "decided": 3,
        "n": 3,
        "refusals": [
          {
            "group": "CO",
            "code": "16",
            "label": "Claim lacks information or has a submission/billing error",
            "known": true,
            "label_source": "paraphrase",
            "root_cause": "missing_information",
            "root_cause_mapped": true,
            "times": 1,
            "of_decided": 3,
            "refused": 150,
            "remark_codes": [
              {
                "code": "N290",
                "times": 1
              }
            ],
            "pattern": false,
            "fix": {
              "key": "correct",
              "label": "Correct the claim and send a replacement",
              "words": "The payer says something on the claim was missing or wrong. Fix it and send a replacement claim (frequency code 7) that carries the payer's claim number. An appeal is not the route for this.",
              "letter_kind": null
            },
            "latest": {
              "remittance_date": "2026-10-18",
              "reference": "PW-2003",
              "payer_claim_number": "PAYER-CN-23",
              "source": "remittances[0]"
            }
          },
          {
            "group": "CO",
            "code": "29",
            "label": "Time limit for filing has expired (timely filing)",
            "known": true,
            "label_source": "paraphrase",
            "root_cause": "timely_filing",
            "root_cause_mapped": true,
            "times": 1,
            "of_decided": 3,
            "refused": 150,
            "remark_codes": [],
            "pattern": false,
            "fix": {
              "key": "timely",
              "label": "Appeal with proof of timely filing",
              "words": "The payer says the claim was filed after its limit. If you filed in time, appeal with your proof of filing, such as the acceptance acknowledgment (999 or 277CA) for the original claim and its date. A replacement claim cannot cure a filing l…",
              "letter_kind": "timely_filing"
            },
            "latest": {
              "remittance_date": "2026-10-18",
              "reference": "PW-2006",
              "payer_claim_number": "PAYER-CN-26",
              "source": "remittances[0]"
            }
          }
        ]
      },
      "same_diagnosis": null,
      "same_code_other_modifiers": []
    },
    {
      "index": 1,
      "procedure_code": "87880",
      "modifiers": [],
      "diagnoses": [
        "J029"
      ],
      "verdict": "refused_before",
      "words": "Refused before, not enough history for a pattern: this payer refused 87880 with no modifier on 1 of 1 decided claim with a line of this shape, and no reason came up on 2 claims or more.",
      "same_shape": {
        "refused": 1,
        "paid": 0,
        "other": 0,
        "decided": 1,
        "n": 1,
        "refusals": [
          {
            "group": "CO",
            "code": "16",
            "label": "Claim lacks information or has a submission/billing error",
            "known": true,
            "label_source": "paraphrase",
            "root_cause": "missing_information",
            "root_cause_mapped": true,
            "times": 1,
            "of_decided": 1,
            "refused": 65,
            "remark_codes": [
              {
                "code": "N290",
                "times": 1
              }
            ],
            "pattern": false,
            "fix": {
              "key": "correct",
              "label": "Correct the claim and send a replacement",
              "words": "The payer says something on the claim was missing or wrong. Fix it and send a replacement claim (frequency code 7) that carries the payer's claim number. An appeal is not the route for this.",
              "letter_kind": null
            },
            "latest": {
              "remittance_date": "2026-10-18",
              "reference": "PW-2003",
              "payer_claim_number": "PAYER-CN-23",
              "source": "remittances[0]"
            }
          }
        ]
      },
      "same_diagnosis": null,
      "same_code_other_modifiers": []
    }
  ],
  "floor": {
    "pattern_min_denials": 2,
    "words": "A reason is called a pattern only when this payer refused a line of the same shape under it on at least 2 claims in the files sent, each claim counted once however many of its lines it refused, and a remittance sent twice counted once. Ever…"
  },
  "root_cause_source": {
    "list": "X12 claim adjustment reason codes",
    "url": "https://x12.org/codes/claim-adjustment-reason-codes",
    "read_on": "10/19/2026",
    "grouping": "Payerworks' own grouping of each code by its published words; the codes' words are X12's and are not copied here."
  },
  "reads": "Only the files sent in this call: nothing is stored, no payer is asked, and nothing here says how a payer will decide."
}
```

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 |
| --- | --- | --- |
| `reference` | string or null |  |
| `payer_ids_compared` | list of string |  |
| `history` | object |  |
| `lines` | list of object |  |
| `floor` | object |  |
| `root_cause_source` | object |  |
| `reads` | string |  |

### POST /v1/denials/prioritize

**Rank your open denials.** Send the 835s holding your open denials, and if you have them, your appeal outcomes (payer_id, carc, outcome). Denials due in 14 days or fewer come first, soonest first; then each by its dollars, or its dollars times your own overturn rate for that payer and code once you have five decided appeals there. A passed deadline comes last. Without outcomes it ranks by deadline and dollars, and says so. No rate here is anyone’s but yours.

Request

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

With the JavaScript client

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

With the Python client

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

request.json

```json
{
  "remittances": [
    "ISA*00*          *00*          *ZZ*SAMPLEPAYER    *ZZ*SAMPLEBILLER   *261018*0900*^*00501*000000093*0*T*:~\nGS*HP*SAMPLEPAYER*SAMPLEBILLER*20261018*0900*93*X*005010X221A1~\nST*835*0001~\nBPR*I*80*C*ACH*CCP*01*999999999*DA*123456*1234567890**01*999999999*DA*654321*20261018~\nTRN*1*EFT-20261018-02*1234567890~\nN1*PR*SAMPLE HEALTH PLAN*PI*99999~\nN1*PE*SAMPLE FAMILY MEDICINE*XX*1234567893~\nLX*1~\nCLP*PW-2003*1*215*0**12*PAYER-CN-23*11~\nNM1*QC*1*PATIENT*SAMPLE****MI*W123456789~\nDTM*232*20261001~\nSVC*HC:99213*150*0~\nDTM*472*20261001~\nCAS*CO*16*150~\nLQ*HE*N290~\nSVC*HC:87880*65*0~\nDTM*472*20261001~\nCAS*CO*16*65~\nLQ*HE*N290~\nCLP*PW-2004*1*350*0**MB*PAYER-CN-24*11~\nNM1*QC*1*PATIENT*SECOND****MI*1EG4TE5MK73~\nDTM*232*20261002~\nSVC*HC:99214*350*0~\nDTM*472*20261002~\nCAS*CO*50*350~\nLQ*HE*N115~\nCLP*PW-2005*1*240*80*100*12*PAYER-CN-25*11~\nNM1*QC*1*PATIENT*THIRD****MI*W555555555~\nDTM*232*20261003~\nSVC*HC:99213*240*80~\nDTM*472*20261003~\nCAS*CO*45*60~\nCAS*PR*1*100~\nAMT*B6*180~\nCLP*PW-2006*1*150*0**12*PAYER-CN-26*11~\nNM1*QC*1*PATIENT*FOURTH****MI*W666666666~\nDTM*232*20260101~\nSVC*HC:99213*150*0~\nDTM*472*20260101~\nCAS*CO*29*150~\nSE*39*0001~\nGE*1*93~\nIEA*1*000000093~"
  ],
  "facts": {
    "filing_limit_days": 180,
    "appeal_limit_days": 180
  },
  "today": "2026-10-19"
}
```

The answer (200)

```json
{
  "today": "2026-10-19",
  "denials": [
    {
      "source": "remittances[0]",
      "index": 1,
      "reference": "PW-2004",
      "payer_claim_number": "PAYER-CN-24",
      "payer_id": "99999",
      "payer_name": "SAMPLE HEALTH PLAN",
      "service_date": "2026-10-02",
      "remittance_date": "2026-10-18",
      "verdict": "denied",
      "lead": {
        "group": "CO",
        "code": "50",
        "label": "Non-covered: not deemed a medical necessity",
        "known": true,
        "root_cause": "medical_necessity"
      },
      "at_stake": 350,
      "at_stake_cents": 35000,
      "deadline": {
        "kind": "redetermination",
        "date": "2027-02-20",
        "date_us": "02/20/2027",
        "days_left": 124,
        "source": {
          "kind": "regulation",
          "citation": "42 CFR 405.942(a)",
          "url": "https://www.ecfr.gov/current/title-42/section-405.942",
          "verified_on": "10/19/2026",
          "presumed_receipt_citation": "42 CFR 405.942(a)(1)"
        },
        "passed": false
      },
      "tier": "open",
      "overturn": null,
      "expected_recovery": null,
      "expected_recovery_cents": null,
      "ranked_by": "dollars",
      "next_step": {
        "key": "necessity",
        "action": "appeal",
        "label": "Appeal with the clinical records",
        "words": "The payer says the service was not medically necessary. Appeal with the clinical records that support it, the ones the payer's coverage policy asks for.",
        "is_letter": true,
        "letter_kind": "medical_necessity",
        "letter_facts": {
          "payer_name": "SAMPLE HEALTH PLAN",
          "patient_name": "SECOND PATIENT",
          "payer_claim_number": "PAYER-CN-24",
          "patient_control_number": "PW-2004",
          "service_date": "2026-10-02",
          "billed_amount": 350,
          "remittance_date": "2026-10-18",
          "adjustment_codes": [
            "CO-50"
          ],
          "remark_codes": [
            "N115"
          ],
          "claim_filing_indicator": "MB"
        },
        "letter_facts_missing": [
          "member_id",
          "provider_name"
        ]
      },
      "rank": 1
    },
    {
      "source": "remittances[0]",
      "index": 0,
      "reference": "PW-2003",
      "payer_claim_number": "PAYER-CN-23",
      "payer_id": "99999",
      "payer_name": "SAMPLE HEALTH PLAN",
      "service_date": "2026-10-01",
      "remittance_date": "2026-10-18",
      "verdict": "denied",
      "lead": {
        "group": "CO",
        "code": "16",
        "label": "Claim lacks information or has a submission/billing error",
        "known": true,
        "root_cause": "missing_information"
      },
      "at_stake": 215,
      "at_stake_cents": 21500,
      "deadline": {
        "kind": "timely_filing",
        "date": "2027-03-30",
        "date_us": "03/30/2027",
        "days_left": 162,
        "source": {
          "kind": "caller",
          "field": "filing_limit_days",
          "value": 180
        },
        "passed": false
      },
      "tier": "open",
      "overturn": null,
      "expected_recovery": null,
      "expected_recovery_cents": null,
      "ranked_by": "dollars",
      "next_step": {
        "key": "correct",
        "action": "correct",
        "label": "Correct the claim and send a replacement",
        "words": "The payer says something on the claim was missing or wrong. Fix it and send a replacement claim (frequency code 7) that carries the payer's claim number. An appeal is not the route for this.",
        "is_letter": false,
        "letter_kind": null
      },
      "rank": 2
    }
  ],
  "counts": {
    "open": 3,
    "due_soon": 0,
    "passed": 0,
    "no_deadline_known": 0,
    "closed_left_out": 0,
    "repeats_left_out": 0,
    "ranked_by_expected_recovery": 0,
    "ranked_by_dollars": 3,
    "outcomes_read": 0
  },
  "ranking": "Deadline first: a denial due in 14 days or fewer comes first, soonest first, and one whose deadline has passed comes last. Then by dollars at stake only: no appeal outcomes were sent, so no overturn rate is used. Send outcomes ({ payer_id, …",
  "deadline_rule": "Every deadline cites its source, as POST /v1/denials/next-step does.",
  "floor": {
    "overturn_min_decided": 5,
    "levels": [
      "payer_and_code",
      "payer_and_root_cause"
    ]
  },
  "letters": "POST /v1/appeals/letter writes the letter each next step names (letter_kind).",
  "reads": "Only the files and outcomes sent in this call: nothing is stored, no payer is asked, and no rate here is anyone's but yours."
}
```

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 |
| --- | --- | --- |
| `today` | string |  |
| `denials` | list of object |  |
| `counts` | object |  |
| `ranking` | string |  |
| `deadline_rule` | string |  |
| `floor` | object |  |
| `letters` | string |  |
| `reads` | string |  |

## Reading the answer

- Every figure carries its `n`: how many claims or lines it rests on. Below a floor, the answer says there is not enough history rather than calling anything a pattern.
- There is no outside benchmark: Payerworks holds no one else’s data to compare yours with, so no answer says how others do.

## Troubleshooting

- `too_much_to_analyze` (413): More was sent than one call reads; the error names what was counted and the limit. Send it in parts.
- `invalid_fee_schedule` (422): A fee schedule row is not usable; each problem is listed with its field, and nothing is judged.

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 |
| --- | --- | --- |
| `invalid_analytics_request` | 400 | A field of the request is not in the shape this route takes; each problem is listed with its field. |
| `no_claim_to_check` | 400 | Send the claim to check as claim, in the 837P JSON shape POST /v1/claims/validate takes. |
| `no_remittances` | 400 | Send the 835s as remittances: a list of X12 files, each the file's text. |
| `nothing_to_summarize` | 400 | Send the claims (claims or x12_837) with their acknowledgments and remittances, or the remittances alone. |
| `too_much_to_analyze` | 413 | More was sent than one call reads: the error names what was counted (`unit`) and the most one call takes (`limit`). Send fewer files a call. |
| `invalid_fee_schedule` | 422 | One or more fee schedule rows are not usable; nothing was judged. Each problem is listed with its field. |
| `invalid_outcomes` | 422 | One or more appeal outcomes are not usable; nothing was ranked. Each problem is listed with its field. |
| `invalid_risk_claim` | 422 | The claim cannot be compared: each problem is listed with its field. Nothing was read. |
| `not_an_835` | 422 | This is not an 835 remittance. |

## Example

[analytics.mjs](https://payerworks.com/docs/examples/analytics.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.

analytics.mjs

```js
// Payerworks example: analytics from your own claims and 835s. Docs: https://payerworks.com/docs/analytics
//
// 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 analytics.mjs
// A test key and invented data only. Payerworks sends nothing to a payer: every file it writes is yours.
import { pathToFileURL } from 'node:url';
import { Payerworks, PayerworksError } from './payerworks.mjs';
import { SENDER_ID, claimEndingIn, refusalOf } from './sample-data.mjs';

/** @param {Payerworks} pw */
export async function run(pw) {
  // 1. A small book through the sandbox payer, so there are 835s to count.
  const sandbox = await pw.postSandboxClaims({ sender_id: SENDER_ID, claims: [claimEndingIn(''), claimEndingIn('DENY'), claimEndingIn('PEND')] });

  // 2. Count what your own remittances show. Every figure carries how many it rests on.
  const summary = await pw.postAnalyticsSummary({ remittances: [sandbox.files.remittance_835] });

  // 3. The failure path: nothing to count is refused, never answered as zero.
  const refused = await refusalOf(() => pw.postAnalyticsSummary({}), { PayerworksError });

  // Two decisions are fewer than the floor, so the denial rate is said with enough_history: false.
  const { decided, denied, enough_history: enoughHistory } = summary.denials;
  return { denials: { decided, denied, enough_history: enoughHistory }, floor: summary.floor.min_n, 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
{
  "denials": {
    "decided": 2,
    "denied": 1,
    "enough_history": false
  },
  "floor": 5,
  "refused": {
    "status": 400,
    "code": "nothing_to_summarize",
    "request_id": "req_3"
  }
}
```
