# Acknowledgments

> What the front end said about your file and each claim in it, as the part of your data to correct, and where each claim stands.

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

A 999 says whether the receiver could read your file; a 277CA says, claim by claim, whether the payer’s front end accepted it. POST /v1/acknowledgments/explain says every rejection in either as the fix: the segment, the loop, the element, and the part of your claim data to correct, matched to your claim reference.

POST /v1/claims/track takes the claims and every 999, 277CA and 835 you hold for them, and answers where each claim stands, who said so, what was paid, and a dated timeline of every file. It reads only what you send: nothing is stored and no payer is asked.

A payer’s 277 request for additional information may ride with the acknowledgments: a request no 275 answers holds the claim as `information_requested`, with its due day. Send your solicited 275s as `attachments` to see whether they answer it.

New here? Start with [A sandbox claim, to its 835](https://payerworks.com/docs/quickstart-sandbox-claim).

## Routes

- [POST /v1/acknowledgments/explain](#post-v1-acknowledgments-explain): Explain an acknowledgment
- [POST /v1/claims/track](#post-v1-claims-track): Track claims

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/acknowledgments/explain

**Explain an acknowledgment.** Every rejection in a 999 or a 277CA said as the fix: the segment, the loop, the element, and the part of your claim data to correct, matched to your claim reference. A TA1 is said as its accept or reject and its note code, in words.

Request

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

With the JavaScript client

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

With the Python client

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

request.json

```json
{
  "x12": "ISA*00*          *00*          *ZZ*SAMPLEPAYER    *ZZ*SAMPLEBILLER   *261018*0900*^*00501*000000081*0*T*:~\nGS*HN*SAMPLEPAYER*SAMPLEBILLER*20261018*0900*81*X*005010X214~\nST*277*0001*005010X214~\nBHT*0085*08*B1*20261018*0900*TH~\nHL*1**20*1~\nNM1*PR*2*SAMPLE HEALTH PLAN*****46*99999~\nTRN*1*B1~\nHL*2*1*21*1~\nNM1*41*2*SAMPLE SLEEP DENTISTRY*****46*SAMPLEBILLER~\nTRN*2*B1~\nHL*3*2*19*1~\nNM1*85*2*SAMPLE SLEEP DENTISTRY*****XX*1234567893~\nHL*4*3*PT~\nNM1*QC*1*PATIENT*SAMPLE****MI*W123456789~\nTRN*2*PW-1004~\nSTC*A7:562:85*20261018*U*2900~\nDTP*472*D8*20261001~\nHL*5*3*PT~\nNM1*QC*1*PATIENT*SECOND****MI*W987654321~\nTRN*2*PW-1005~\nSTC*A2:20*20261018*WQ*350~\nREF*1K*PAYER-CN-5~\nDTP*472*D8*20261002~\nSE*22*0001~\nGE*1*81~\nIEA*1*000000081~"
}
```

The answer (200)

```json
{
  "kind": "277CA",
  "batch": {
    "trace": "B1",
    "statuses": [],
    "accepted": null,
    "rejected": null
  },
  "claims": [
    {
      "reference": "PW-1004",
      "payer_claim_number": "",
      "verdict": "R",
      "fixes": [
        {
          "category": "A7",
          "code": "562",
          "entity": "85",
          "verdict": "R",
          "where": "the billing provider in your claim data",
          "sentence": "The claim was rejected for invalid information: the billing provider's NPI is missing or invalid (STC A7:562:85). Fix it on the billing provider in your claim data, then send the claim again."
        }
      ]
    },
    {
      "reference": "PW-1005",
      "payer_claim_number": "PAYER-CN-5",
      "verdict": "A",
      "fixes": [
        {
          "category": "A2",
          "code": "20",
          "entity": "",
          "verdict": "A",
          "where": "",
          "sentence": "The claim was accepted into the payer's adjudication system: accepted for processing (STC A2:20). Nothing to fix."
        }
      ]
    }
  ]
}
```

| Field | Type | What it is |
| --- | --- | --- |
| `kind` | one of "999", "277CA", "TA1" |  |
| `interchanges` | list of Ta1Read | A TA1: each one, in words. |
| `source` | object | A TA1: where the note codes’ words come from, and the day read. |
| `groups` | list of object | A 999: each functional group and set. |
| `batch` | object | A 277CA: the batch’s own answer. |
| `claims` | list of object | A 277CA: each claim. |

### POST /v1/claims/track

**Track claims.** Send the claims (as JSON or the 837P) and the 999s, 277CAs and 835s you hold for them. Each claim comes back with where it stands (submitted, received, accepted, rejected, processed, denied or unknown), who said so, what was paid, and a dated timeline of every file. It reads only what you send: nothing is stored and no payer is asked. A payer’s 277 request for additional information may ride with the acknowledgments: a request no 275 answers holds the claim as information requested, with its due day; send your solicited 275s as attachments to see whether they answer it.

Request

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

With the JavaScript client

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

With the Python client

```python
answer = pw.post_claims_track(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~GE*1*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~AK9*A*1*1*1~SE*6*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*2900~QTY*90*1~AMT*YU*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~SE*23*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*1392*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~SE*19*0001~GE*1*3~IEA*1*000000003~"
  ]
}
```

The answer (200)

```json
{
  "as_of": null,
  "reads": "Only the files sent in this call: nothing is stored, and no payer is asked.",
  "claims": [
    {
      "index": 0,
      "reference": "PW-SBX-PAID",
      "references": [
        "PW-SBX-PAID"
      ],
      "state": "processed",
      "meaning": "Adjudicated: the payer accepted responsibility for the claim. That is not the same as paid in full, or paid at all.",
      "next_step": "Read the remittance for the amount paid, the patient's share and each adjustment (POST /v1/remittances/explain).",
      "status_reported_by": "payer",
      "since": "2026-10-19",
      "days_in_state": 0,
      "decided_by": {
        "timeline_index": 3,
        "source": "remittances[0]",
        "kind": "835"
      },
      "paid_total": 1392,
      "paid_total_cents": 139200,
      "has_reversals": false,
      "crossover": null,
      "payer_claim_numbers": [
        "PWS2292711347"
      ],
      "submissions": [
        {
          "index": 0,
          "source": "x12_837[0]",
          "reference": "PW-SBX-PAID",
          "frequency_code": "1",
          "replaces_payer_claim_number": null,
          "submitted_on": "2026-10-19",
          "batch_id": "PW-SBX-PAID",
          "charge": 2900,
          "service_date": "2026-10-01",
          "service_date_end": null,
          "member_id": "W123456789",
          "payer_id": "99999",
          "latest": true
        }
      ],
      "lines": {
        "submitted": [
          {
            "index": 0,
            "line_control_number": null,
            "procedure_code": "E0486",
            "charge": 2900,
            "service_date": "2026-10-01",
            "acknowledgment_statuses": [],
            "payments": []
          }
        ],
        "remittance_lines_not_matched": [
          {
            "source": "remittances[0]",
            "payer_claim_number": "PWS2292711347",
            "clp_status": "1",
            "procedure_code": "E0486",
            "billed": 2900,
            "paid": 1392,
            "allowed": 1740,
            "line_control_number": null
          }
        ]
      },
      "timeline": [
        {
          "at": "2026-10-19",
          "source": "x12_837[0]",
          "kind": "837P",
          "event": "submitted",
          "reported_by": null,
          "words": "Submitted as an original claim.",
          "submission": 0,
          "counts_for_state": true,
          "not_counted_because": null,
          "matched_by": null,
          "detail": {
            "reference": "PW-SBX-PAID",
            "frequency_code": "1",
            "replaces_payer_claim_number": null,
            "batch_id": "PW-SBX-PAID",
            "charge": 2900
          }
        },
        {
          "at": "2026-10-19",
          "source": "acknowledgments[0]",
          "kind": "999",
          "submission": 0,
          "counts_for_state": true,
          "not_counted_because": null,
          "matched_by": "control_numbers",
          "event": "file_accepted",
          "reported_by": "unknown",
          "words": "The receiver read the transaction set that carried this claim (999 IK5 A). That says the file was taken, not that the claim was accepted: the claim acknowledgment (a 277CA) says that.",
          "detail": {
            "group_control_number": "1",
            "transaction_set_control_number": "0001",
            "verdict": "A"
          }
        }
      ],
      "correlation": {
        "matched_by": [
          "control_numbers",
          "reference"
        ],
        "split_payer_claim_numbers": null,
        "notes": []
      }
    }
  ],
  "unmatched": [],
  "duplicate_remittances": []
}
```

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 |
| --- | --- | --- |
| `as_of` | string or null |  |
| `reads` | string |  |
| `claims` | list of TrackClaim |  |
| `unmatched` | list of object | File by file, each item nothing matched, with why. |
| `duplicate_remittances` | list of object | An 835 sent twice (the same TRN02 and TRN03): counted once, named here. |
| `attachments` | list of object | Each 275 sent, and the claims whose requests it answers. |
| `loinc_notice` | string | Only when a 277 request for additional information is among the files. |

## Reading the answer

- Each claim in an explain answer has `fixes`, each a `sentence` naming the field of your claim data to correct.
- A tracked claim’s `state` is one of the states listed below, with its `meaning`, its `next_step`, who reported it (`status_reported_by`) and the `timeline` of every file that led there. A receipt is never an acceptance, and a claim with no answer among your files is `submitted`, never assumed accepted.

## Troubleshooting

- `not_an_acknowledgment` (422): This route reads 999s, 277CAs and TA1s. A claim status 277 (005010X212) is refused here, naming the route that reads it.
- `not_read_here` (422): A 997 is recognized and named (`kind: "997"`), but not read.
- `mixed_functional_groups` (422): One interchange that holds two kinds of file is refused whole, naming both; split it and send each.
- `too_much_to_track` (413): Tracking reads at most what one call can hold; the error names what was counted and the limit.

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 |
| --- | --- | --- |
| `ambiguous_claims` | 400 | Send the claims once: as `claims` or as `x12_837`, not both. |
| `invalid_track_request` | 400 | A field of the request is not in the shape it takes; each problem is listed with its field. |
| `no_claims_to_track` | 400 | Send the claims to track as `claims` (a list) or `x12_837` (an 837P interchange, or a list of them). |
| `no_x12` | 400 | Send the file's text as `x12`. |
| `too_much_to_track` | 413 | More was sent than one call tracks: the error names what was counted (`unit`) and the most one call takes (`limit`). Send fewer claims or files a call. |
| `invalid_tracked_claims` | 422 | One or more claims cannot be tracked; nothing was read. Each problem is listed with its field. |
| `mixed_functional_groups` | 422 | This interchange holds functional groups of different kinds: send each kind in its own interchange, so no part of it goes unread. |
| `not_a_tracked_file` | 422 | A file is not a kind its list takes: x12_837 holds 837P claims, acknowledgments holds 999s, 277CAs and 277 requests for additional information, remittances holds 835s, attachments holds solicited 275s. The error names the file and its kind. |
| `not_an_acknowledgment` | 422 | This is not a 999, a 277CA or a TA1. |
| `not_read_here` | 422 | This file is a kind Payerworks recognizes but does not read here yet; the error names its kind. |

## Code lists

### Where a tracked claim stands

| State | What it means | The next step |
| --- | --- | --- |
| `submitted` | Sent, and no acknowledgment of this submission is among the files you sent. | Wait for the claim acknowledgment (a 277CA) from the receiver or the payer, then track the claim again with it. |
| `received` | Received, and not yet accepted for adjudication: a receipt is not an acceptance. | Wait for the payer to accept or reject it. Some payers never send an explicit acceptance, so a claim can stay received; if it does, ask the payer with a claim status inquiry (POST /v1/claim-status/276 writes one). |
| `accepted` | Accepted into the payer's adjudication system, and being processed. | Wait for the remittance (an 835). A claim status inquiry says where it stands in the meantime. |
| `information_requested` | The payer asked for more information before it decides the claim (a 277 request for additional information), and no 275 among the files you sent answers it. | Send what the payer asked for as a solicited 275 by its due day (POST /v1/attachments/requests/explain lays it out, POST /v1/attachments/275 writes it, POST /v1/attachments/requests/check checks it), then track the claim again with that 275 under attachments. |
| `rejected` | Rejected before adjudication: the payer never decided it, and no remittance follows a rejection. | Fix what the acknowledgment names (POST /v1/acknowledgments/explain says each fix) and send the claim again as a new submission. |
| `processed` | Adjudicated: the payer accepted responsibility for the claim. That is not the same as paid in full, or paid at all. | Read the remittance for the amount paid, the patient's share and each adjustment (POST /v1/remittances/explain). |
| `denied` | Adjudicated and denied: the payer decided it is not responsible for the claim. | Read why and what to do next (POST /v1/denials/next-step): correct it and send a replacement claim, or appeal. |
| `unknown` | No single state can be read from the files: the answers are mixed or incomplete, or use codes a claim acknowledgment does not carry. | Read the timeline, and ask the payer with a claim status inquiry before you act. |

## Example

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

acknowledgments.mjs

```js
// Payerworks example: acknowledgments, every rejection said as the fix. Docs: https://payerworks.com/docs/acknowledgments
//
// 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 acknowledgments.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. Two claims through the sandbox payer: one paid, one rejected at its front end (a member ID ending in REJ).
  const claims = [claimEndingIn(''), claimEndingIn('REJ')];
  const run1 = await pw.postSandboxClaims({ sender_id: SENDER_ID, claims });

  // 2. Explain the 277CA it answered: each rejection as the part of your claim data to correct.
  const ack = await pw.postAcknowledgmentsExplain({ x12: run1.files.claim_acknowledgment_277ca });
  // A rejected claim's verdict is R, the 277CA's own letter.
  const rejected = ack.claims.find((c) => c.verdict === 'R');

  // 3. Track both claims from the files you hold: where each stands, and who said so.
  const tracked = await pw.postClaimsTrack({
    claims,
    acknowledgments: [run1.files.acknowledgment_999, run1.files.claim_acknowledgment_277ca],
    remittances: [run1.files.remittance_835],
  });

  // 4. The failure path: a remittance is not an acknowledgment, and the route says which route reads it.
  const refused = await refusalOf(() => pw.postAcknowledgmentsExplain({ x12: run1.files.remittance_835 }), { PayerworksError });

  return {
    rejected: { reference: rejected?.reference, fix: rejected?.fixes[0]?.sentence },
    states: Object.fromEntries(tracked.claims.map((c) => [c.reference, c.state])),
    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
{
  "rejected": {
    "reference": "EX-REJ",
    "fix": "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."
  },
  "states": {
    "EX-PAID": "processed",
    "EX-REJ": "rejected"
  },
  "refused": {
    "status": 422,
    "code": "not_an_acknowledgment",
    "request_id": "req_4"
  }
}
```
