Skip to content

For developers

Headless. Every capability is an API call.

Today Payerworks is an engine you use beside your current clearinghouse: nothing is sent to a payer until that payer’s route opens, and none is open yet. Payerworks’ engine has no screen you have to use. Get a test key on this page, run claims, eligibility questions and claim status questions through the sandbox payers, and from your own system write 837P, 837I, 837D, 270 and 276 files and read every remittance, acknowledgment, eligibility answer and status answer as the next step.

Get a test key

For invented test data only. We email you a link, and the link shows your key once, on this page. A live key needs a signed business associate agreement first.

Used to send the link and to know whose key it is. Nothing else.

Principles

How it works

  • Built on the code we bill with

    The remittance reader, the claim status reading and the NPI check are the modules the billing company behind Payerworks runs on its own claims and remittances, imported, never copied. The rest, the 837P writer, the 837I and 837D writers, the claim readers and edits, the payer directory, the eligibility and denial explainers, claim tracking, enrollment, the sandbox payers and your account, is Payerworks’ own code, written for this API.

  • Errors you can act on

    Every refusal comes back with a stable code, the path of the field, and a sentence a person can read. A rejection from a payer comes back the same way.

  • Honest about sending

    The API builds, checks and reads every file, and the sandbox payers answer your test files end to end. It does not yet send a file to a real payer: transmission opens one payer at a time as Payerworks’ own routes go live, and until then you send on the connection you already have.

  • Test it without a call

    A test key comes from this page and the sandbox payer answers it right away, with no meeting and no contract. A live key needs a signed business associate agreement first, because a live file carries real patients.

  • Answers pushed to you

    Set a webhook and every answer the 837P claims sandbox makes, and every change to an enrollment request, is posted to your server as it is made, signed under the Standard Webhooks specification, so any of its libraries checks it. An event your server did not take is tried again, and every event can be listed and sent again for 30 days. Up to ten more webhook destinations, each with its own secret and its own event types, are each tried again on their own schedule for more than three days.

What is kept, and for how long

A claim, a file or a remittance you send is answered and forgotten: never stored, never logged. A batch job is the one exception you ask for by name: it keeps your file until the job finishes and its result for 24 hours after, then deletes both. What is kept is a count of your key’s calls; your webhook’s address and signing secret; each webhook event queued for you, for 30 days, which holds the sandbox’s invented answer and, from what you sent, only your claim references and your file’s control numbers, or for an enrollment event only its ids, statuses and words (up to 2,000 events a UTC day); and for 24 hours a fingerprint of each request sent with an Idempotency-Key, never the request itself. Your webhook destinations (each one’s name, address, event types and signing secret, and what was posted to it, for as long as its event is kept), the address you set for notices and your keys’ names are kept until you remove them. Enrollment is the one part of the API that keeps what you send: the providers and enrollment requests you create (names, NPIs, tax ids, contacts and notes) and the PDFs you upload for a task, because a request lives for weeks and Payerworks people work it. They are for a practice’s business facts; never put a patient’s information in them. A test key’s providers and requests expire 30 days after their last change, and its PDFs 30 days after each was uploaded, or about a day after you delete one; a PDF uploaded but never confirmed goes after a day, whatever the key. A live key’s are kept until you delete them, and the copy of a deleted live PDF for 30 days more. Apart from that, our backups hold every PDF that was there each night, a test key’s included, for 35 days, and each Sunday’s for a year.

Test end to end

The sandbox payers

Five sandboxes answer the way a payer and its front end would. Claims sent to POST /v1/sandbox/claims come back in the answer to one call as a 999, a 277CA and an 835, every file explained and, once you set a webhook, posted as events too; institutional and dental claims sent to POST /v1/sandbox/claims/837i and /837d come back the same way, with no events. Eligibility questions sent to POST /v1/sandbox/eligibility come back as a 271, claim status questions sent to POST /v1/sandbox/claim-status as a 277, and prior authorization requests sent to POST /v1/sandbox/authorizations as a 278 response, each explained, with no events. Enrollment has the fifth, the sandbox payer PWSANDBOX, below. Every file is a test file. Use invented patients only.

How the subscriber’s member ID ends decides each answer, and each sandbox reads only its own list.

Claims POST /v1/sandbox/claims

  • RejectedMember ID ends in REJ

    A member ID ending in REJ is rejected by the payer’s front end. The 277CA refuses the claim with STC A7:164:IL (the subscriber’s member number is invalid), and no remittance follows.

  • DeniedMember ID ends in DENY

    A member ID ending in DENY is accepted on the 277CA and then denied on the 835 the way payers send a refusal: claim status 1 (processed), nothing paid, the whole charge adjusted under CO 50, and each line carrying remark N115, so only the adjustments show the refusal.

  • With the payerMember ID ends in PEND

    A member ID ending in PEND is accepted on the 277CA and stays with the payer, so no remittance line comes back yet.

  • Paid, to the deductibleMember ID ends in DED

    A member ID ending in DED is accepted and paid, but most of what the payer allows is applied to the patient’s deductible (PR 1), so the payer pays only the rest, the patient owes the deductible, and the remainder of the charge is a contractual write-off (CO 45).

  • Paid, less a copayMember ID ends in COPAY

    A member ID ending in COPAY is accepted and paid less a fixed copay the patient owes (PR 3), and the remainder of the charge is a contractual write-off (CO 45).

  • Refused for missing informationMember ID ends in D16

    A member ID ending in D16 is accepted and then refused on the 835 for missing information: nothing is paid, every line is adjusted under CO 16 with remark M51 (a procedure code is missing or invalid), and the claim status code says processed, so only the adjustments show the refusal.

  • A line bundledMember ID ends in D97

    A member ID ending in D97 is paid except for its last line, which the payer bundles into another service (CO 97, remark M15) and pays nothing for. A claim of one line has that line bundled, so nothing is paid.

  • Reversed and paid againMember ID ends in REV

    A member ID ending in REV comes back as a correction: the 835 first reverses an earlier payment on the claim (claim status 22, every amount negated), then pays the claim again on a higher allowed amount, so the check carries only the difference.

  • Paid, with a recoveryMember ID ends in PLB

    A member ID ending in PLB is paid, and its check also carries a provider-level adjustment: the payer recovers an overpayment on an earlier claim (PLB reason WO), so the check is less than the claims it pays.

  • Paid and forwardedMember ID ends in XOVER

    A member ID ending in XOVER is paid as primary and forwarded to the patient’s secondary payer (claim status 19, the secondary named as the crossover payer, remark MA18), so it is not filed to the secondary by hand.

  • Information asked forMember ID ends in RFAI

    A member ID ending in RFAI is accepted on the 277CA and then held for a document: the payer sends a 277 request for additional information asking for a progress note by a day thirty days later, and pays nothing until a solicited 275 answers it. Send the same claims again, to the same sandbox route (professional, institutional or dental), with that 275 in attachments_275, and the claim is paid as any other.

  • PaidAny other member ID

    Any other member ID is accepted and paid: the payer allows part of the charge, the patient owes coinsurance on what was allowed (PR 2), the rest is a contractual write-off (CO 45), and every service line carries its share to the cent.

Eligibility POST /v1/sandbox/eligibility

  • Not foundMember ID ends in NOTFOUND

    A member ID ending in NOTFOUND is not found: the 271 refuses in the subscriber’s loop with AAA 75 and states no benefits.

  • Birth date does not matchMember ID ends in BADDOB

    A member ID ending in BADDOB is found, but the birth date does not match: AAA 71 in the patient’s loop, and no benefits.

  • Payer cannot answerMember ID ends in DOWN

    A member ID ending in DOWN reaches a payer that cannot answer: AAA 42 in the payer’s loop, follow-up R. Ask again later, unchanged.

  • Coverage endedMember ID ends in INACTIVE

    A member ID ending in INACTIVE answers with coverage that ended last year: EB 6 on service type 30, with last year’s plan dates.

  • Out of network onlyMember ID ends in OON

    A member ID ending in OON is active with out-of-network benefits only: every figure is stated for the out-of-network tier.

  • Prior authorizationMember ID ends in AUTH

    A member ID ending in AUTH is active, and each service asked about requires prior authorization (EB11 Y).

  • Deductible metMember ID ends in MET

    A member ID ending in MET is active with the deductible met: the remaining deductible is zero.

  • Another payer firstMember ID ends in OTHER

    A member ID ending in OTHER is active, and another payer is primary: an EB R line names it in a 2120 loop.

  • ActiveAny other member ID

    Any other member ID is active, with the full grid: individual and family deductible and out-of-pocket limit for the calendar year, in and out of network, each with what remains, plus coinsurance and a copay per visit on each service asked.

Each 271 the eligibility sandbox writes can be read back in the JSON grouped by plan as well: send it to POST /v1/eligibility/explain with ?include=answer&shape=plans, and the same answer is explained the same from either.

Claim status POST /v1/sandbox/claim-status

A claim status question follows the claims list, so it agrees with that claim’s 277CA and 835: the same payer claim number and the same paid amount. These endings are the status sandbox’s own, the answers a status question alone can get:

  • Not found yetMember ID ends in NOFILE

    A member ID ending in NOFILE is not found: the payer has no claim on file for the question (A4:35). It is never a rejection.

  • No answer in timeMember ID ends in SLOW

    A member ID ending in SLOW gets no answer in time: the payer’s system did not respond (E2:689:PR), so there is nothing to record and the question is asked again on the next business day.

  • Records asked forMember ID ends in INFO

    A member ID ending in INFO is pended for information the payer already asked the provider for (P3:123:1P), and the answer names it: medical notes or a report (P3:297:1P).

  • No payment comingMember ID ends in NOPAY

    A member ID ending in NOPAY is finalized with no further payment forthcoming (F4:1). The claims sandbox pays the same claim, and the answer states what it paid: nothing more is coming.

One exception: when a claim’s payment depends on its service lines (member IDs ending in D97), the status answer says the claim is finalized and leaves what was paid to the remittance, because a status question carries no lines. The claim status sandbox answers test files only: a 276 marked for production use is refused.

Prior authorization POST /v1/sandbox/authorizations

It answers from the same payer as the eligibility sandbox, and the endings the two share answer alike: a member the eligibility sandbox cannot find is not found here either, and a claim the claims sandbox denies is not certified here. These are its endings:

  • Not foundMember ID ends in NOTFOUND

    A member ID ending in NOTFOUND is not found: the response refuses in the subscriber’s loop with AAA 75 and reviews nothing.

  • Birth date does not matchMember ID ends in BADDOB

    A member ID ending in BADDOB is found, but the birth date does not match: AAA 71 in the patient’s loop, and nothing reviewed.

  • Payer cannot answerMember ID ends in DOWN

    A member ID ending in DOWN reaches a payer that cannot answer: AAA 42 in the payer’s loop, follow-up R. Send it again later, unchanged.

  • Member ID refusedMember ID ends in REJ

    A member ID ending in REJ is refused as a member ID the payer does not accept: AAA 72 in the subscriber’s loop, the same refusal the claims sandbox gives.

  • Not covered that dayMember ID ends in INACTIVE

    A member ID ending in INACTIVE is not certified because the patient was not covered on the date of service: HCR A3 with reason 53, as the eligibility sandbox ends that coverage last year.

  • No authorization neededMember ID ends in NOTREQ

    A member ID ending in NOTREQ needs no authorization: HCR NA with reason 0H, and no certification number.

  • Approved in partMember ID ends in PARTIAL

    A member ID ending in PARTIAL is certified in part: each service asked for two or more units is approved for half of them, rounded down (HCR A2, reason 10); a service of one unit is approved in full.

  • Approved, other datesMember ID ends in MODDATE

    A member ID ending in MODDATE is certified with changes (HCR A6): the dates approved start two weeks after the dates asked and run for thirty days.

  • DeniedMember ID ends in DENY

    A member ID ending in DENY is not certified as not medically necessary (HCR A3, reason 0F), as the claims sandbox denies the claim.

  • More information asked forMember ID ends in INFO

    A member ID ending in INFO is pended until the payer has more patient information (HCR A4, reason 0U), and a message names the visit notes it needs.

  • Pended for reviewMember ID ends in PEND

    A member ID ending in PEND is pended while the first utilization review runs (HCR A4, reason 30), with an administrative reference number to follow it by.

  • ApprovedAny other member ID

    Any other member ID is certified in total (HCR A1) with a certification number, every service approved for the units asked, effective from the first date asked for at least ninety days.

Every answer is a test interchange from the payer id PWSANDBOX. Run them in your browser.

curl "$PAYERWORKS_BASE_URL/v1/sandbox/claims" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "sender_id": "SAMPLEBILLER", "claims": [ … ] }'

270 and 271

Eligibility, asked and explained

Write the 270 from the JSON your system already holds, send it on your own connection, and give Payerworks the 271 that comes back, as X12 or as JSON. The answer is said per patient, in words.

  • A deductible or out-of-pocket figure is read only inside its own network tier, coverage level, insurance type and period. When a payer states several, such as Medicare Part A and Part B, or last year and this year, each is said under its own type and dates, and none is dropped.
  • In network and out of network are the plan’s benefit tiers as the payer stated them. A 271 does not say whether a provider participates with the plan, and the answer never says it does.
  • A refusal is said as the next step by the payer’s own follow-up code: correct these fields, ask again later, or do not send it again.
  • Whether the plan covered the day you asked about is read inside both the plan’s dates and the member’s own eligibility dates, from the later begin to the earlier end, so a member whose coverage ended before the plan year did is not covered after it. Where a payer states coverage separately for each insurance type, such as Medicare Part A active and Part B not, each is said with its own state.
  • A figure the payer did not state is null, never zero.
  • The answer may come as an X12 271, as the flat JSON list of benefits most clearinghouse APIs return, or as that JSON grouped by plan with each kind of benefit in its own list. The three forms of one answer explain the same; what the grouped shape holds that is not read is listed as not read, and an entry the sender set aside is read as the payer stated it, and said.
  • A question can ask about procedures as well as service types. One that asks only about procedures asks no service type beside them, and a question that uses no procedure field is written exactly as before.
# Write the 270 from your JSON, send it on your own connection, then explain the 271 that comes back.
curl "$PAYERWORKS_BASE_URL/v1/eligibility/270" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" -H "Content-Type: application/json" \
  -d @question.json
curl "$PAYERWORKS_BASE_URL/v1/eligibility/explain" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile x12 answer.271 '{ x12: $x12, asked: { service_type_codes: ["30", "98"] } }')"

276 and 277

Claim status, asked and explained

Write the 276 from the JSON your system holds, send it on your own connection, and give Payerworks the 277 that comes back, as X12 or as JSON. Each question is answered with where the claim stands, the money, the next step and when to ask again.

  • A claim the payer cannot find in its first few business days is not loaded yet, never rejected; after that, the answer says to confirm the payer received it.
  • A payer whose system did not answer is asked again the next business day. A question the payer refused because of its own data is corrected first, never asked again unchanged.
  • Finalized is not the same as paid: a finalized answer that states no amount leaves the money to the remittance.
  • Business days are Monday to Friday, and holidays are not counted.
  • The payer’s own claim number (REF*1K) and the clearinghouse’s trace number (REF*D9) are said apart, so one is never taken for the other.
  • A file holding more than one call can read is refused before it is read, naming what was counted and the limit: split it.
# Write the 276, send it on your own connection, then explain the 277 that comes back.
curl "$PAYERWORKS_BASE_URL/v1/claim-status/276" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" -H "Content-Type: application/json" \
  -d @inquiry.json
curl "$PAYERWORKS_BASE_URL/v1/claim-status/explain" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile x12 answer.277 '{ x12: $x12, asked: { submitted_on: "2026-10-05" } }')"

837P, 999, 277CA and 835

Where a claim stands, from the files you hold

Send the claim (JSON or the 837P), and the 999s, 277CAs and 835s you hold for it. Payerworks 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.

submitted
Sent, and no acknowledgment of this submission is among the files you sent.
received
Received, and not yet accepted for adjudication: a receipt is not an acceptance.
accepted
Accepted into the payer's adjudication system, and being processed.
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.
rejected
Rejected before adjudication: the payer never decided it, and no remittance follows a rejection.
processed
Adjudicated: the payer accepted responsibility for the claim. That is not the same as paid in full, or paid at all.
denied
Adjudicated and denied: the payer decided it is not responsible for the claim.
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.
  • A remittance decides first; then the acknowledgments, a rejection before anything else and the payer’s word before a clearinghouse’s; then the newest date. A late receipt never hides a rejection.
  • An accepting 999 moves nothing: it says the file was read, not that the claim was taken.
  • Only the latest submission of a claim counts, and a corrected claim joins the claim whose payer claim number it names.
  • An 835 sent twice is counted once and named, never added to the paid total twice.
  • A reversal whose original decision is not among the files you sent is named on the timeline but left out of the paid total.
curl "$PAYERWORKS_BASE_URL/v1/claims/track" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile c claims.837 --rawfile a ack.277 --rawfile r remit.835 '{ x12_837: $c, acknowledgments: [$a], remittances: [$r] }')"

835 and appeals

Denials, said as the next step

Send an 835, or the claims as JSON, and each claim comes back with what the payer did and what to do about it.

  • The verdict is read from the money and the adjustment codes, never the claim status code alone: a refusal usually arrives as “processed as primary” with a CO-50 or a CO-16 beside it.
  • Whether the patient may be billed is said in dollars, by group: patient responsibility may be billed, a contractual adjustment may not.
  • A correction is said as the fields to send on the replacement claim, and a request for records names what to send.
  • A deadline is computed only from a regulation it cites (Medicare fee-for-service, by its filing indicator) or from the limits you send. Otherwise its date is null, and its words say the payer’s contract or provider manual states it.
  • An appeal letter is written by template from your facts only, and a letter you edit can be checked against them before it goes.
  • An underpayment letter states one of two bases, from your figures: a contracted amount above what the payer allowed, or a payment below the allowed amount less the patient’s share. With neither, no letter is written.
  • A Medicare late filing (CO-29) is said as not appealable, and a Medicare duplicate (CO-18) as a reopening, each with the regulation it rests on. A Medicare filing limit counts from the earliest service line’s date, and a Medicare redetermination or reopening window from the remittance advice’s own date (DTM*405), else the payment date (BPR16).
# Every claim in an 835, said as the next step.
curl "$PAYERWORKS_BASE_URL/v1/denials/next-step" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile x12 remittance.835 '{ x12: $x12, facts: { filing_limit_days: 90 } }')"

# An appeal letter from your facts, by its kind.
curl "$PAYERWORKS_BASE_URL/v1/appeals/letter" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" -H "Content-Type: application/json" \
  -d @letter.json

Follow-up, remittances and your own numbers

After the claim is sent

Follow up on open claims, check the remittances that come back, and count what your own files show. Each answer reads only what you send in the call.

  • A payer’s days to pay, its denial rate or an overturn rate is learned from your files alone, with how many it rests on, and is read as a pattern only past a floor (five paid claims, five decided appeals, two refusals of one reason). Below the floor the answer says there is not enough history. Payerworks holds no data of its own about any payer, so nothing here is a benchmark.
  • A month is the calendar month; the current month counts to today.
  • A claim the payer’s search did not find gets the next questions to ask in order, each built from your own question and the facts you hold, never a value nobody gave.
  • Two checks are one payment only when their trace number and its originator agree, and a deposit is matched by amount and date only when exactly one check fits.
  • A line with no rate in your fee schedule is said as no rate given, never read as zero, and Medicare’s published rate is labeled as Medicare’s, never as your contract.

278

Prior authorization, written and read

Write the 278 from JSON, send it the way the payer takes it, and give Payerworks the response that comes back. Payerworks sends nothing.

  • Whether a payer takes a 278 at all is the payer’s choice: many take prior authorizations only on a portal, by fax or by phone. Every answer that writes a file says so.
  • A response is read by its decision code: a request the payer never reviewed is said as not reviewed, never as denied, and a pend that asks for something is said as waiting on more information.
  • The certification number goes on the claim by the request’s kind: an authorization’s as the prior authorization number, a referral’s as the referral number, and only when something was approved.
  • The Medicare lists are dated copies of CMS’s own, said with their dates, and a code on neither is not on the lists held, never not required. A CPT code is held by its number only.
  • The FHIR output is the Da Vinci PAS bundle’s shape, built from the guide’s published pages and examples; it has not been run through the HL7 validator.

Estimates

What a patient may owe

Estimates, a Good Faith Estimate, an equipment replacement window and a poverty guideline percent, each figured from what you send.

  • An estimate never supplies an allowed amount, and a benefit the 271 did not state is said as not stated, never read as zero.
  • Whether a copay replaces the deductible and coinsurance, or comes first, is plan design a 271 does not carry: you say which.
  • A Good Faith Estimate is your document. Payerworks lays it out with every element the regulation lists, and issues, delivers and keeps nothing.
  • The replacement window checks no Medicare record, and the poverty guideline is the 2026 table, named and dated; another year is refused.

CMS-1500, PDFs, appeal forms and the 275

Documents, forms and attachments

The paper side of the same claims and answers, drawn from what you send: the CMS-1500, a remittance laid out for reading, an eligibility answer as a summary, CMS’s own Medicare appeal forms filled in, and a claim’s attachments as an X12 275. Each PDF comes back in the answer as base64 with its SHA-256; Payerworks keeps no copy and sends nothing.

  • A CMS-1500 is the claim POST /v1/claims/837p would write: the same checks refuse it the same way, and the form draws the repaired claim. Six service lines go on a form and a claim takes as many forms as it needs, each Box 28 the total of its own lines. Boxes 12 and 31 are dated only when you send signed_on.
  • form: full draws CMS’s own form under the data; data_only draws the data alone, to print onto pre-printed red forms. A value too wide for its box is set smaller, never cut, and the answer names it.
  • The remittance and the eligibility summary are drawings of the explain answers, POST /v1/remittances/explain and POST /v1/eligibility/explain, never a second reading: every figure is the one those routes give, and a figure the file did not state is left blank.
  • The Medicare appeal forms are CMS’s published files, unaltered but for the fields typed in: the redetermination request (CMS-20027) and the reconsideration request (CMS-20033), for Original Medicare only. Nothing is signed, dated or ticked for you; the deadline comes with the regulation it is counted under, and a request past it with no reason given is warned.
  • A 275 carries each attachment under the control number the claim’s PWK segment announced with transmission EL, and a control number the claim did not announce is refused. A claim that announces an attachment this way is told so when it is validated, so it does not wait for a file that never comes.
  • The 275 is written as public payer companion guides show it, not from the licensed implementation guide, and the answer says so: check your payer’s own guide before you send one.
  • A LOINC code you give for an attachment’s kind is checked by its check digit, and named in LOINC’s own words where Payerworks holds it.

LOINC

This material contains content from LOINC (http://loinc.org). LOINC is copyright © Regenstrief Institute, Inc. and the Logical Observation Identifiers Names and Codes (LOINC) Committee and is available at no cost under the license at http://loinc.org/license. LOINC® is a registered United States trademark of Regenstrief Institute, Inc.

Read with a model, switched off

Paper EOBs and card photos

Two routes read a picture with a model: a paper explanation of benefits into a draft 835, and an insurance card into the payer, the member ID and the payer directory’s rows. Both are built and switched off until Payerworks turns them on, route by route (until then each answers 501 model_read_not_offered, which is not retried), and then answer test keys only until an agreement covers the model for live data.

  • A paper EOB comes back as a draft, never an ERA, and nothing is posted: every value with the reader’s confidence and where on the page it was read. The payments are checked against the check total the page states by arithmetic, never by the model, and a difference is said with both figures. The draft 835 is a test interchange whatever your key, and an element the page did not print is left empty and named.
  • A card is read twice, and a member ID the two reads spell differently is said. A plan type is given only beside words the card prints, and an electronic payer ID printed on the card is shown only where a payer publishes it, because one misread character is another payer’s ID. A match in the directory says the directory holds published facts about that payer, never that a claim can be sent to it.
  • Send one page, or two at most, up to 1.4 MB. A read that cannot finish inside one call is refused with what to send instead; it is never kept to finish later.
  • An account’s keys together may make 100 reads a UTC day on each route. A call refused for the route being off, a live key or a body with no file is not counted. Neither route takes an Idempotency-Key, so a second call is a second read.
  • With a test key, send invented documents only. Nothing stops a real EOB or a real card from being sent with one, and no business associate agreement covers the model for it, so never send one. The picture is read and forgotten: never stored, never logged.

Rules, standard charges files and the sandbox over days

Your own checks

Three routes for what your own system must hold to: your own claim rules, a hospital’s standard charges file, and the claims sandbox over the days a real claim takes.

  • POST /v1/edits/rules/check takes rules you write for your own payers: a field’s path, an operator from a fixed list of twenty, a value, your message and whether it is an error or a warning. Each rule is checked for the format and applied to the claims you send with it, and the same rules may be sent as rules on POST /v1/claims/validate and POST /v1/claims/837p, where one of severity error refuses the claim and 837p writes nothing. A rule fires on the fault it names, says your own words and is marked source: caller, so it never reads as a Payerworks edit.
  • A rule’s pattern is a small fixed format, never code, and each call has a step budget: a rule it does not reach is listed as not checked, never as passed. Nothing is stored.
  • POST /v1/price-transparency/validate holds a hospital’s standard charges file, the tall or wide CSV or the JSON, to CMS’s version 3.0.0 template and schema, read from CMS’s own repository at one commit. Each problem names its row or path, the column and the rule; Payerworks’ own advice comes as warnings, never as a fault. Nothing is hosted, published or kept.
  • POST /v1/sandbox/claims/timeline spreads the claims sandbox over simulated days: the 999 after five minutes, a 277CA saying each claim was received after an hour, the payer’s own 277CA after two days and the 835 after fourteen. Send a later as_of and the cursor you were given to read what came next; a poller steps the clock, and a webhook handler is tested by posting each item’s event to it. Nothing is stored: the same seed, claims and start give the same run, and Payerworks posts none of its events.

Providers, payers and tasks

Enrollment, the way a payer asks for it

Three things stand between a provider and a payer’s files. Credentialing is the payer checking the provider. Payer enrollment is the provider joining the payer’s plans. Both are the provider’s own work with each payer, and Payerworks does neither. Transaction enrollment registers a provider to exchange one kind of file with one payer through one clearinghouse. That is what these routes ask Payerworks for. Payerworks is not yet registered with any payer, so today a request waits with Payerworks and nothing is sent; the sandbox payer runs the whole path so you can build against it now.

  • Make a provider once: its name, billing NPI, tax id and contacts. Then one request per provider, payer and transaction.
  • Every request says whose turn it is: yours, the provider’s, Payerworks’ or the payer’s. When it is the provider’s, its tasks say what to do, in rank order, each with its instructions, links and fields of text or a PDF. Payerworks’ own tasks are listed too, so a task held up by one of them names it.
  • Every request keeps the history of each status change, and every change raises one event, numbered by sequence, so a receiver can order and dedupe them without trusting the order they arrive in.
  • A request can be changed until it is sent to a payer, and canceled in any open status. Every POST takes an Idempotency-Key, so a retried create answers the same request.

The four transactions

eligibility
Eligibility and benefits: Payerworks writes the 270 and reads the 271.270/271
claim_status
Claim status: Payerworks writes the 276 and reads the 277.276/277
professional_claims
Professional claims: Payerworks writes and checks the 837P.837P
remittances
Remittances: Payerworks reads and explains the 835.835

A request for anything else is refused (transaction_not_built), because Payerworks does not yet enroll for it: institutional_claims, dental_claims, attachments, eft, coordination_of_benefits, insurance_discovery, medicare_number_lookup. 999 and 277CA acknowledgments are never enrolled on their own: they come back on the route the claim went.

Each status, and whose turn it is

  • draftYour turn

    Being written. Only this key sees it.

    You: submit, change or delete it.

  • payerworks_action_requiredPayerworks’ turn

    Submitted; Payerworks’ turn. reason_code says why.

    Payerworks staff on the desk, or the sandbox.

  • provider_action_requiredThe provider’s turn

    One or more provider tasks are open.

    You, completing the tasks in rank order.

  • with_payerThe payer’s turn

    Payerworks sent the request to the payer and follows up. Never set while the route is not open.

    Payerworks staff, recording the payer’s answer.

  • liveNobody’s turn

    The payer confirmed. Files of this kind may flow.

    Payerworks staff, if the payer later ends it.

  • rejectedYour turn

    The payer, or Payerworks before sending, refused it. rejection holds the words and the remedy.

    None: fix it and file a new request.

  • canceledNobody’s turn

    Ended at your request or by Payerworks before it went live.

    None.

  • endedNobody’s turn

    Was live; the payer ended the route.

    None: a new request starts again.

You never write a status: no write route takes one.

Why a request is Payerworks’ turn

A request at payerworks_action_required carries reason_code, said in these words ({x12} and {payer} are the request’s):

route_not_open
Waiting on Payerworks. Payerworks is not yet registered to exchange {x12} files with {payer}, so nothing has been sent to the payer. Your request waits here, and you can change or cancel it until it is sent.
preparing
Waiting on Payerworks. Payerworks is preparing what the payer asks for.
reviewing
Waiting on Payerworks. Payerworks is checking the answers the provider gave.

Today Payerworks has no payer route open yet, so no live request is with a payer or live. A live request you submit goes to payerworks_action_required with reason_code route_not_open, and the answer to the create or the submit says so in a warning. From there Payerworks staff may ask the provider for a task, or reject or cancel it, but nobody can mark it with the payer or live until the route opens.

What each payer asks

  • GET /v1/enrollment-requirements/{payer_id} answers for every payer id: per transaction, whether Payerworks’ route is open, who files, the fields needed and each step the payer publishes, with its source and the day Payerworks read it.
  • Payerworks holds published steps for the four Medicare DME MACs and CMS’s HETS. Every other payer answers published: false, which means Payerworks holds no facts for it, never that nothing is required.
  • Payerworks states no timeframe of its own. Where a payer publishes one, it is quoted with its source.

The sandbox payer, end to end

With a test key, enroll a provider with the sandbox payer, PWSANDBOX. The last word of the provider’s name picks what happens (make one provider per scenario), and POST /v1/sandbox/enrollments/{id}/advance makes the sandbox payer’s next move. A scenario takes the same path every time, and a call retried with the same Idempotency-Key answers the same ids. The sandbox payer’s task instructions and the words of its rejection start with “Sandbox:”, and so do a request’s status_words once it is with the sandbox payer, live, rejected or ended. A cancel asked while a request is with the sandbox payer or live is what the next advance does: canceled, or ended.

  • Any other word

    Sandbox: a provider whose name ends in any other word goes live in one call: submitting moves it to payerworks_action_required (preparing), then with_payer, then live, with an event for each move. Advance has nothing to do.

  • Provider name ends in WAITS

    Sandbox: a provider whose name ends in WAITS waits at payerworks_action_required (route_not_open) and stays there. That is the state every live request is in today, so build that screen first.

  • Provider name ends in TASKS

    Sandbox: a provider whose name ends in TASKS gets two provider tasks on submit: rank 0 asks for a packet number (an identifier), rank 1 is steps done elsewhere, closed with no values. Once both are complete it waits for review; advance moves it to with_payer, then live.

  • Provider name ends in SIGN

    Sandbox: a provider whose name ends in SIGN gets one task with a document field, signed_form, and an invented one-page form as its template. Download the template, upload a signed PDF, complete the task; advance moves it to with_payer, then live.

  • Provider name ends in BLOCKED

    Sandbox: a provider whose name ends in BLOCKED gets a Payerworks task at rank 0 and a provider task at rank 1. Completing rank 1 first answers 409 task_blocked, naming the Payerworks task. Advance completes the Payerworks task; complete yours; advance moves it to with_payer and live.

  • Provider name ends in REPLACE

    Sandbox: a provider whose name ends in REPLACE starts as TASKS. The first advance deletes the open rank 0 task and adds a new one in its place (enrollment.task.deleted with superseded_by, then enrollment.task.assigned).

  • Provider name ends in REJECT

    Sandbox: a provider whose name ends in REJECT goes with_payer on submit; advance rejects it with the payer’s words and the remedy, carried whole.

  • Provider name ends in ENDS

    Sandbox: a provider whose name ends in ENDS goes live on submit; advance ends the live route.

  • Provider name ends in SLOW

    Sandbox: a provider whose name ends in SLOW goes with_payer on submit and stays there until advance moves it to live.

import { Payerworks } from "./payerworks.mjs";

const pw = new Payerworks({ apiKey: process.env.PAYERWORKS_API_KEY, baseUrl: process.env.PAYERWORKS_BASE_URL });
// A test key enrolls with the sandbox payer; the last word of the name picks the scenario.
const provider = await pw.postProviders({ name: "Sample Family Clinic TASKS", npi: "1234567893", tax_id: "12-3456789", tax_id_type: "ein" });
let enr = await pw.postEnrollments({
  provider_id: provider.id, payer: { id: "PWSANDBOX" }, transaction: "remittances", submit: true,
  notify_email: "dev@sampleclinic.example",
  primary_contact: { organization_name: "Sample Family Clinic LLC", email: "enrollment@sampleclinic.example", phone: "8015550100",
    address: { line1: "100 Main St", city: "Salt Lake City", state: "UT", zip: "84101" } },
});
console.log(enr.status, enr.waiting_on, enr.status_words);
for (const t of enr.tasks.filter((t) => t.responsible === "provider" && !t.complete)) {
  const values = t.fields.map((f) => ({ key: f.key, text: "PKT-0001" }));
  ({ enrollment: enr } = await pw.postTasksById(t.id, { values, complete: true }));
}
// A PDF for a document task: the upload and the confirm in one helper.
// await pw.uploadDocument(enr.id, task.id, "signed_form", "signed-form.pdf", pdfBytes);
const { moved } = await pw.postSandboxEnrollmentsByIdAdvance(enr.id);
console.log(moved);

Enrollment events

Each change raises one event, posted to your webhook and listed by GET /v1/events. An event carries ids, statuses and words only, never a tax id, a contact, a note or a document: GET /v1/enrollments/{id} for the rest. Payerworks also emails your key’s notice address (PUT /v1/account/contact) when a request moves without your doing it, to provider_action_required, with_payer, live, rejected, canceled or ended: every such move inside the same 15 minutes goes in one email, which names each request’s id, the provider’s name and NPI, the payer, the transaction and the status, and never a tax id, a contact, a note or a document. With no notice address set, nothing is emailed.

enrollment.updated
An enrollment request changed: a status change no type below names, or its fields, tasks, documents or notes. It carries the status, the previous status, whose turn it is, the reason, the fields that changed and the sequence.
enrollment.live
The payer confirmed the request: files of this kind may flow. It adds when, and the payer’s reference.
enrollment.rejected
The request was refused. It adds the rejection: the payer’s words whole, and the remedy.
enrollment.canceled
The request was canceled, or you asked to cancel one that is with the payer or live (cancel_requested says which).
enrollment.ended
A live route ended: the payer ended it, or a cancel you asked for while it was live went through (in the sandbox, at the next advance).
enrollment.task.assigned
A task was added: its id, rank, who holds it, its title and its fields.
enrollment.task.completed
A task was finished, by the provider, or by Payerworks for its own.
enrollment.task.deleted
A task was replaced by another (superseded_by names it).

What is refused, and why

  • A Social Security number, as a tax id or anywhere: a provider enrolls with its EIN, which the IRS issues (ssn_not_accepted). A tax id whose first two digits are not a prefix the IRS assigns to an EIN is refused too (not_an_ein); that catches typos and some Social Security numbers, never all of them, so never send one.
  • A bank account, routing or account number: no task asks for one, and free text holding a run of eight or more digits is refused (holds_long_number).
  • A phone that is all one digit or not a US number, and on a live key an email at a reserved or example domain: the payer’s mail and calls would go nowhere.
  • A real payer with a test key (test_key_sandbox_payer_only), and the sandbox payer with a live key (sandbox_payer_test_only).
  • Eligibility at a DME MAC: traditional Medicare eligibility is asked of CMS’s HETS (ask_another_payer, naming the payer that answers it).
  • A second open request for the same provider, payer and transaction (409 enrollment_exists, naming the one that stands).
  • An upload that is not a PDF, or is over 10 MB.
Every field problem

A refusal that lists problems (invalid_provider, invalid_enrollment, invalid_task_values) names each one’s code and field. The codes:

unknown_field
A field this request does not take; nothing is dropped silently.
required
A required field is missing.
invalid_type
A field is not of the type it takes.
too_short
Text shorter than the field takes.
too_long
Text longer than the field takes.
invalid_npi
An NPI is 10 digits with a valid check digit.
invalid_tax_id
A tax id is 9 digits, with or without one hyphen after the second digit.
invalid_tax_id_type
tax_id_type is "ein".
ssn_not_accepted
A Social Security number is never accepted: a provider enrolls with an EIN, which the IRS issues.
not_an_ein
The first two digits are not a prefix the IRS assigns to an EIN.
invalid_email
An email address in the form name@domain.
reserved_email_domain
On a live key, an email at a reserved or example domain, which can never receive mail.
invalid_phone
A phone is 10 digits, area code first; the area code and the exchange do not start with 0 or 1.
phone_not_real
A phone that is all one digit is not a number anyone answers.
invalid_phone_extension
An extension is 1 to 6 digits.
contact_name_form
A contact gives organization_name, or first_name and last_name, not both.
invalid_state
A two-letter US state, DC or territory code.
invalid_zip
A ZIP is 5 digits, or 9 with one hyphen allowed.
too_many_contacts
At most 5 contacts.
holds_long_number
Free text may not hold a run of 8 or more digits: nothing that could be a bank or account number is kept.
invalid_payer_id
A payer id is 1 to 80 letters and digits.
test_key_sandbox_payer_only
A test key enrolls with the sandbox payer, PWSANDBOX, only.
sandbox_payer_test_only
The sandbox payer answers test keys only.
invalid_transaction
transaction is eligibility, claim_status, professional_claims or remittances.
transaction_not_built
Payerworks does not yet enroll for this kind of file.
ask_another_payer
This payer does not answer this transaction; the problem names the payer that does.
ptan_required
This payer’s published steps need the provider’s PTAN.
invalid_ptan
A PTAN is 4 to 10 letters and digits; for a DME MAC, exactly 10 digits.
invalid_date
A day written YYYY-MM-DD.
effective_date_out_of_range
The requested effective date is from today to 180 days ahead.
effective_date_past_45_days
A DME MAC remittance request asks an effective date at most 45 days after it is submitted (CEDI’s rule).
invalid_era_aggregation
era_aggregation is "tin" or "npi".
era_aggregation_remittances_only
era_aggregation applies to a remittances request only.
field_locked
This field changes only while the request is a draft.
unknown_field_key
The task has no field with this key.
value_type_mismatch
A text field takes text; a document field takes a document_id.
text_too_long
A task’s text value is at most 2,000 characters.
missing_required_value
Completing a task needs a value for every required field.
document_not_uploaded
The document has not been uploaded and confirmed.
document_not_on_task
The document was uploaded for another task or field.
invalid_filename
A filename ends .pdf and is 1 to 120 characters of letters, digits, spaces, dots, dashes and underscores.
invalid_field_key
A field key is lower snake case, 1 to 40 characters.
field_key_refused
A task field may never ask for a bank, routing or account number, or a Social Security number.
invalid_link
A link is https, to a public host.
invalid_value
A value this field does not take.

Many requests from one file

POST /v1/enrollments/import/check reads a CSV of up to 100 rows, one request a row, and checks each exactly as POST /v1/providers and POST /v1/enrollments check their bodies, saving nothing; POST /v1/enrollments/import then makes each valid row. The header is the template below, and a column the import does not take is refused by name, never dropped.

provider_name,npi,tax_id,tax_id_type,payer_id,payer_name,transaction,contact_organization_name,contact_first_name,contact_last_name,contact_email,contact_phone,contact_phone_extension,contact_address_line1,contact_address_line2,contact_city,contact_state,contact_zip,notify_email,requested_effective_date,era_aggregation,ptan,note,submit
  • A provider your key already has with that NPI and tax id is used as it is, never changed, and the row says so. A request that already stands, or an earlier row of the file asking the same, is a duplicate that names it.
  • The import stops starting rows when its time in one call runs out, and names the first row not run: send that row and the rows after it again. An Idempotency-Key retry of the same file answers the same requests, never duplicates of them.
  • With a live key, each NPI is looked up in the NPI registry, which answers one of three ways: it holds an active record (with Type 1 or 2 and whether the row’s provider name matches, each row on its own), it holds none, or it could not be asked just now. Each is a warning, never a refusal, and nothing it says is kept.
  • GET /v1/enrollments/export answers every request matching the list’s filters as CSV, up to 5,000 rows, with no contact, note or full tax id; a cell a spreadsheet would run as a formula starts with an apostrophe.
  • GET /v1/enrollments/durations says how long your own requests spent waiting on Payerworks, on the provider and with the payer, by payer and transaction. A figure is given once five stays have finished, the median and the middle half, each with how many it rests on; fewer says not enough history. Payerworks has no figures from anyone else.
The import template’s columns
provider_name
The provider's name, 2 to 100 characters.Required.
npi
The billing NPI, 10 digits with a valid check digit. Write it as text so a spreadsheet keeps every digit.Required.
tax_id
The provider's EIN, 9 digits, with or without the hyphen after the second.Required.
tax_id_type
ein (the only kind taken). Left blank: ein.
payer_id
The payer ID, as text: leading zeros are part of it.Required.
payer_name
The payer's name, up to 80 characters.
transaction
eligibility, claim_status, professional_claims or remittances: one a row.Required.
contact_organization_name
The primary contact's organization; or give contact_first_name and contact_last_name.
contact_first_name
The primary contact's first name.
contact_last_name
The primary contact's last name.
contact_email
The primary contact's email.Required.
contact_phone
The primary contact's phone, 10 digits.Required.
contact_phone_extension
1 to 6 digits.
contact_address_line1
The contact's street address.Required.
contact_address_line2
A suite or unit.
contact_city
The city.Required.
contact_state
A two-letter state, DC or territory code.Required.
contact_zip
5 digits, or 9 with one hyphen allowed.Required.
notify_email
Where Payerworks writes about this request.Required.
requested_effective_date
YYYY-MM-DD, from today to 180 days ahead.
era_aggregation
tin or npi, remittances only.
ptan
4 to 10 letters and digits; for a DME MAC exactly 10 digits, and required.
note
Up to 500 characters for Payerworks staff. Never a patient's information.
submit
true submits the request at once; false or blank makes a draft.

How each row ends

ready
Checked: it would be made as it is (the check route only).
created
Made: its request (and its provider, when the key had none with that NPI and tax id).
invalid
Not made: one or more cells would be refused. Each problem names its column.
duplicate
Not made: an open request for this provider, payer and transaction already stands on the key, or an earlier row of this file asks for the same one.
refused
Not made: the store refused it (a limit reached, a lost race, a store failure). The error says which; send the row again.
not_run
Not started: the import ran out of time first. Send this row and the rows after it again.
Every problem a file can have

A file with any of these is refused whole, 422 invalid_import, before any row is checked:

csv_required
The body is { csv: "..." }: the file's text, its first line the header.
csv_too_long
A CSV is at most 500,000 characters.
unterminated_quote
A quoted cell is not closed before the file ends.
stray_quote
A quote appears inside a cell that is not quoted; quote the whole cell and write a quote inside it twice.
no_rows
The file has a header and no rows.
too_many_rows
A file holds at most 100 rows after the header; split it.
unknown_column
A header names a column the import does not take; nothing is dropped silently.
duplicate_column
Two header cells name the same column.
missing_column
A column every import needs is not in the header.
wrong_cell_count
A row has more cells than the header.
cell_too_long
A cell is at most 600 characters.
unknown_body_field
The body takes csv only.

What Payerworks will never do

  • Sign for a provider. The provider signs its own agreements with each payer; no field here asks Payerworks to.
  • Ask for a Social Security number, a bank account or a routing number.
  • Say a request is with a payer, or live, before it is.

Providers and requests belong to the key that made them, like its webhook and its events. A new key starts with none, so keep the key that holds them; a rotated key keeps them, because rotation keeps the key id. Enrollment is the one part of the API that keeps what you send: the providers and enrollment requests you create (names, NPIs, tax ids, contacts and notes) and the PDFs you upload for a task, because a request lives for weeks and Payerworks people work it. They are for a practice’s business facts; never put a patient’s information in them. A test key’s providers and requests expire 30 days after their last change, and its PDFs 30 days after each was uploaded, or about a day after you delete one; a PDF uploaded but never confirmed goes after a day, whatever the key. A live key’s are kept until you delete them, and the copy of a deleted live PDF for 30 days more. Apart from that, our backups hold every PDF that was there each night, a test key’s included, for 35 days, and each Sunday’s for a year.

837P, 837I and 837D, not yet open

Claim files over SFTP

Drop 837P, 837I or 837D interchanges in your own folder over SFTP and read the answers from another: a TA1 when the envelope is not yours, a 999 by your setting, and a report that names every claim’s errors as POST /v1/claims/validate names them. It is built and not yet open: until Payerworks opens it, every /v1/sftp route answers 403 sftp_not_open.

  • Make your user with PUT /v1/sftp and add an SSH public key with POST /v1/sftp/ssh-keys. There are no passwords, a private key is refused and never kept, and a key’s comment is dropped.
  • A test key’s user sends usage indicator T to receiver PAYERWORKSTEST and a live key’s sends P to PAYERWORKS, both under qualifier ZZ. The answers are always in your key’s mode, whatever the file said.
  • The 999 says what Payerworks checked of the envelope and the controls. A claim the checks refuse is said in the report, never in the 999, and no 277CA is written for a dropped file.
  • Nothing you drop goes on to a payer.

What is kept, and for how long

  • A file you drop in inbound is read once and deleted as soon as its answers are written. A file the lane could not reach is deleted by the bucket 1 day after it arrived; S3 removes an expired file in the day or so after its expiry.
  • Each answer (a TA1, a 999 and the report) waits in outbound for 7 days after it was written, then the bucket deletes it; you may delete it sooner. S3 removes an expired file in the day or so after its expiry.
  • The bucket keeps no earlier version of a file and is in no backup. Nothing you drop is stored anywhere else, logged or sent on: the lane's own log line names the key, the kind of file and the outcome, never its contents or its name.
  • The endpoint's transfer log in CloudWatch records the name of each file you move, so never put a patient's name or any identifier in a file name.
  • Nothing is sent to a payer: Payerworks validates and acknowledges the file, and you send the claims on yourself.
  • When your key is revoked, its SFTP user is removed at once; when it expires, or when the endpoint could not be reached at the revoke, by the next daily pass, within a day. A file dropped under a revoked or expired key before then is deleted unread, with no answer.

Reference

Routes

Every route is under /v1. The base URL is shown with your key. The whole contract, machine-readable, is openapi.json.

Docs by job, with quickstarts, examples that run and every page as markdown for an agent: payerworks.com/docs. Every rate, size and item limit, read from the code that refuses by it: payerworks.com/limits. What was measured this month, with any incident and its feed: payerworks.com/status.

Claims and files

  • POST/v1/claims/validate

    Validate claims

    Every reason a claim would be refused at the front door or by a payer’s first edit, each with a code, the field’s path and a sentence, from the one edit table GET /v1/edits lists: the NPIs’ check digits, the dates, the pointers, a total that must equal its lines, the filing indicator, a character X12 reserves, and each diagnosis, HCPCS Level II code and modifier against the release in force on its date of service (the ICD-10 diagnosis releases for fiscal 2025 to 2027, CMS’s HCPCS file from January 2025) and the place of service against CMS’s list. A CPT code is checked for its shape only, and a dental D code or a CPT modifier, which CMS’s file does not carry, is listed as not checked. Every claim carries what the guides require: the submitter’s contact, the billing provider’s tax id, the patient’s address and date of birth, a facility’s address when one is sent, and the filing indicator. Formatting is repaired first, on a copy, and each claim lists its repairs (phone punctuation, a ZIP+4 hyphen, letter case, a diagnosis code’s dot, spaces) and not_checked, every check that could not run and why. For a claim to a later payer: the payers’ order, each earlier payer’s paid amount, claim and line balance, the adjudication date and the patient’s remaining share each at one level, the adjustment groups, the Medicare secondary payer type, and remark codes in force on the day; when Medicare is the later payer, the claim’s total equals each earlier payer’s paid amount plus all its adjustments, and a claim to Medicare is an original claim (frequency 1). Send the claims as JSON, or an 837P file as x12, read back claim by claim. Send your own rules beside them as rules, written as POST /v1/edits/rules/check reads them, and each is applied to every claim after its repairs: a rule that fires is listed as caller_rule with source caller, and one of severity error makes the claim invalid like any other error. Passing means the claim is well formed, not that a payer will pay it.

  • POST/v1/claims/837p

    Write an 837P

    Up to one hundred claims written as one 005010X222A1 interchange under your sender and receiver ids and your control number, a claim to a secondary or tertiary payer included: send each earlier payer’s adjudication (otherSubscriberInformation, and lineAdjudicationInformation on each line it paid) and the file carries the coordination of benefits loops. Every claim is checked as POST /v1/claims/validate checks it, your own rules included when you send them as rules, and written from its repaired copy; each claim’s answer lists its repairs, what was not checked, and a not_written warning for any field the 837P has no element for. Nothing is written if any claim would be refused. A test key writes test files only.

  • POST/v1/claims/837i

    Write an 837I

    Up to one hundred institutional claims, the UB-04’s electronic form, written as one 005010X223A2 interchange from the same JSON as the 837P route, with the institutional fields: the facility type and frequency, the statement dates, admission type, source and patient status, revenue code lines, principal and admitting diagnoses with present on admission, procedures, occurrence, span, value and condition codes, and the attending and operating providers. Every claim is checked first, and nothing is written if any would be refused. A revenue code or a type of bill is checked for its shape: the code set’s words belong to the NUBC. A test key writes test files only.

  • POST/v1/claims/837i/validate

    Validate institutional claims

    The same institutional claims checked against the 837I’s rules without writing a file: each problem names its code, its field and a sentence.

  • POST/v1/claims/837d

    Write an 837D

    Up to one hundred dental claims and predeterminations written as one 005010X224A2 interchange from the same JSON as the 837P route, with the teeth and surfaces, the area of the mouth, tooth status, prosthesis replacement, orthodontic months and up to four diagnoses. A predetermination carries no date of service. A dental procedure code is checked for its shape: the code set’s words belong to the ADA. Nothing is written if any claim would be refused, and a test key writes test files only.

  • POST/v1/claims/837d/validate

    Validate dental claims

    The same dental claims checked against the 837D’s rules without writing a file: each problem names its code, its field and a sentence.

  • POST/v1/claims/correction-plan

    Plan a corrected or void claim

    Say whether you want to replace or void a claim, and give the payer’s claim number, or say the payer never processed it (adjudicated: false); with neither, the plan asks which rather than guess. The plan says the frequency code, whether REF*F8 goes, whether to use a new patient control number, and when a Medicare contractor fixes a processed claim by reopening instead. Send the replacement too, as JSON or an 837P (an 837D for a dental claim), and it is checked against the plan and every claim rule. Companion guides vary, and the answer says so. It writes and sends nothing.

  • GET/v1/edits

    Every claim edit

    Every check Payerworks runs on a professional claim before it is written: its code, SNIP type, severity, what is wrong, how to fix it and where the rule comes from. Also the formatting repairs made first, the coordination of benefits checks a claim to a later payer gets, the checks a correction plan’s replacement gets, and the checks Payerworks does not run, with why. Filter with snip_type, severity, kind or runs_in, applied to every list. Passing every check means the claim is well formed and its codes are valid on their dates, not that a payer will pay it.

  • GET/v1/edits/{code}

    One claim edit

    One check by its code, in any case. A check Payerworks does not run answers runs: false and says why; any other code is 404 edit_not_found.

  • POST/v1/x12/parse

    Read an X12 file

    Any X12 file Payerworks reads, to JSON with its envelope, in the file’s own delimiters: an 835, a 999, a 277CA, a 270 or 271, a 276 or 277, a 278, or an 837P, 837I or 837D. A claim file is read back to the same JSON its write route takes, each claim also as its own one-claim interchange, so a bulk file splits per claim, and every segment the reader does not read is named by its id, loop and position, never its value. A 277CA is told from a claim status 277 by its implementation guide, any other kind is named, and an interchange that mixes kinds is refused.

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

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

  • POST/v1/claims/cms1500

    Draw claims on the CMS-1500

    Up to one hundred claims in the 837P’s JSON, checked by the same claim checks as POST /v1/claims/837p (your own rules are not applied here) and drawn on the CMS-1500 (02/12): six service lines a form, as many forms as a claim needs (at most 200 a call), each form’s Box 28 the total of its own lines. form: full puts CMS’s own form under the data; data_only prints the data alone, to print onto pre-printed red forms (print one on plain paper and hold it against a form first). Send signed_on to date Boxes 12 and 31, which are otherwise left undated, and payer_address for the carrier block. The answer is your PDF as base64; nothing is sent or kept.

  • POST/v1/attachments/275

    Write a 275 attachment

    A claim’s attachments as an X12 275 (005010X210): each file (PDF, TIFF, JPEG, PNG or a CDA document, up to ten and 1.4 MB in all) base64 in its own BIN, under the control number the claim’s PWK segment announced with transmission EL, so the payer can match the two; a control number the claim does not announce is refused. A control number is 2 to 50 letters, digits, dots, dashes or underscores, the same in the claim’s PWK06 and the 275’s TRN02. Written as public payer companion guides show the 275, not from the licensed implementation guide, and the answer says so: check your payer’s own. A test key writes test files only. Names an attachment’s LOINC document type when you give one. A solicited 275 answers a payer’s request: send `request` with the request’s trace, and each attachment may echo its own requested code (stc01) and name its service line (line_control_number); POST /v1/attachments/requests/explain gives every value from the request.

  • POST/v1/attachments/requests/explain

    Read a request for more information

    A payer’s 277 request for additional information (005010X213) said claim by claim: what it asks for (each LOINC code it names, in LOINC’s own words, or said as a code Payerworks has no words for), about which claim and which service line, by when against your own today, and the solicited 275 that answers it: the payer’s trace, the claim’s REF*EJ, the submitter id, the payer’s response contact, and a body POST /v1/attachments/275 takes. Read as public sources show the request, not from the licensed implementation guide, and the answer says so. Nothing is stored or sent.

  • POST/v1/attachments/requests/check

    Check a 275 against its request

    A solicited 275 (or up to ten) held to the 277 request it answers before you send it: BGN01 11, the payer’s trace on every attachment, the request’s REF*EJ and submitter id, every code asked for answered by an attachment echoing its STC, each file’s BIN length, and the due day. Errors and warnings per 275 set, and per claim whether every code is answered: a set with an error of its own answers no code, what is missing is read across every 275 sent, and a line’s request is answered only by an attachment carrying that line’s REF*FJ. Nothing is stored or sent.

  • POST/v1/edits/rules/check

    Check your own claim rules

    Rules you write for your own payers: a field’s path, an operator from a fixed list of twenty, a value, the payers it applies to, your message and whether it is an error or a warning. Each rule is checked for the format, and applied to any claims you send with it (up to 100). The same rules may ride on POST /v1/claims/validate and POST /v1/claims/837p as rules, where a rule of severity error refuses the claim. Patterns are a small fixed format, never code, and each call has a step budget, and a field a rule reads may reach at most 1,000 values: a rule it does not reach, or whose field reaches more, is listed as not checked with its reason, never as passed. Nothing is stored.

  • POST/v1/claims/submit

    Send claims to a payer

    Not offered yet: Payerworks is connected to no payer, so this answers 501 submit_not_offered with each gate and whether it is open (sending switched on, a live key, a signed business associate agreement recorded for your account, a trading partner for the payer, and that partner switched on). Nothing is written or sent. Once every gate is open it writes your claims as an 837P exactly as POST /v1/claims/837p does, under the partner’s interchange ids and a control number Payerworks assigns, and hands the file to the partner; transmitted is true only when the partner’s connection accepted the file. Every patientControlNumber begins with your account’s reference prefix, and one already in flight is refused unless it was rejected or the claim is a replacement or a void. The partner’s 999, 277CA and 835 arrive as submission events to the key that sent the file, never to anyone else. Send an Idempotency-Key: a retry under it answers the first answer and sends nothing again.

  • GET/v1/partners

    Trading partners

    The trading partners claims go to through POST /v1/claims/submit today, with the payer ids each takes and the transactions it carries, and one sentence about Payerworks’ connections. Today the list is empty: Payerworks is connected to no payer.

Eligibility

  • POST/v1/eligibility/270

    Write a 270

    Up to one hundred eligibility questions written as one 005010X279A1 interchange, from the JSON shape most clearinghouse APIs take: the payer, the provider, the subscriber and at most one dependent, the day and the service types. Ask about any day, a later one included; with no day named, the payer answers for its own current day. A question may also ask about procedures (CPT, HCPCS, CDT or NDC codes with up to four modifiers), point each to the patient’s diagnoses, and carry the place of service, a prior authorization or referral number, the provider’s taxonomy, a payer PIN and a birth order for twins; a question that asks only about procedures asks no service type beside them. Each question is checked field by field first, and nothing is written if any would be refused. You are told when a member ID begins with the card issuer number (80840), a name holds a title, a degree or a suffix, a backtick was written as an apostrophe, or the day is more than a year back. A question that uses none of these fields is written exactly as before. A test key writes test files only.

  • POST/v1/eligibility/explain

    Explain an eligibility answer

    Any 271, as X12 or as the JSON most clearinghouse APIs return, said per patient: whether the coverage is active on the day you asked about, the plan, and for each service type the deductible and out-of-pocket limit with what is met and what remains, in and out of network, individual and family, the coinsurance, the copay, and what is not covered or needs prior authorization at each tier. A refusal comes back as every reason in words, with the field to fix and where to check it, or when to ask again. The JSON may be either shape most clearinghouse APIs return, a flat list of benefits or benefits grouped by plan with each kind in its own list, and one answer explains the same in any of the three forms; what the grouped shape holds that is not read is named. Add ?include=answer for the 271 read to JSON as well, and &shape=plans to have it in the grouped shape.

  • POST/v1/eligibility/identity-retry

    Find the patient again

    After a payer could not match the patient (reject reasons 65, 67, 71, 72, 73, 75 or 77, sent as reasons or read from the 271 itself): the next questions to try, in order, each built from your own question and the facts you give (the legal first name, a previous last name, a dependent’s own member ID), each with its changes listed and checked as a 270 is. No name is guessed and there is no nickname table; a day and month swapped is offered only as one to check first. Suggestions only: Payerworks looks nobody up, and nothing is sent.

  • POST/v1/eligibility/service-type-test

    Does a payer answer a service type

    Two answers you hold from one payer, one to 30 (or 35, for dental) and one to a specific service type: whether the payer answered the specific one, likely ignored it, answered it under other service types, or refused it, with how many lines it added and removed. It reads only the two answers, as they stood when they were given: a payer may change what it answers.

  • POST/v1/eligibility/coordination

    Which plan pays first

    The plans you list for one person, two to ten, put in the order they pay by the NAIC coordination of benefits model regulation and Medicare’s secondary payer rules, each step naming its rule and its section. Medicaid always pays last, and after Medicare a Medigap policy comes before TRICARE; injury coverage not related to the service is left out of the order. Where a rule needs a fact you did not send, no order is given and the fact is named, never guessed. It knows only the plans you send: it cannot find a person’s other coverage.

  • POST/v1/eligibility/discovery/verify

    Is this coverage the patient’s

    Up to ten coverages an insurance discovery search found, each a 271, checked against the patient: the last name (a two-part surname allowed), the first name, the exact date of birth, the gender and the address. Each is said as a match, one to review or not this patient, and one the payer reports as pending is marked to confirm with the payer. Payerworks runs no discovery search itself.

  • POST/v1/eligibility/pdf

    Print an eligibility answer

    A 271, as POST /v1/eligibility/explain takes it, as a summary per patient: what was asked and what the answer echoes back, whether the coverage is active, the plan’s figures in and out of network, each service type asked, and every benefit line by plan and then service type. Up to 50 patients and 3,000 benefit lines a call, and 3,000 rows as drawn: a benefit line is listed under each service type it names, so a line naming ten service types is ten rows (and each of its messages one more under each). The answer is your PDF as base64.

Claim status

  • POST/v1/claim-status/276

    Write a 276

    Up to one hundred claim status questions, from the JSON shape most clearinghouse APIs take, written as one 005010X212 interchange for you to send on your own connection. Each question is checked field by field first, and nothing is written if any would be refused. A test key writes test files only.

  • POST/v1/claim-status/explain

    Explain a claim status answer

    A 277 claim status answer, as X12 or as the JSON most clearinghouse APIs return, said per question: where the claim stands, whether money is coming and how much, what to do next and when to ask again. A claim the payer cannot find yet is never called a rejection, and a question the payer refused because of its own data is never asked again unchanged. A claim the payer’s search did not find comes back with the next questions to ask, in order, each built from the question you sent (asked.request) and what you hold (asked.known), never from a value you did not give; a payer whose system did not answer, with a retry schedule for the same day.

  • POST/v1/claims/follow-up

    Follow up on open claims

    Your open claims (up to 200) with the day each was sent, and your history of when each payer paid (up to 20,000 rows), or the 837Ps, 277CAs and 835s themselves. Each payer’s days to pay are learned from your history alone, with how many paid claims they rest on, and only from five up; below that the answer says there is not enough history. Each claim comes back with when its remittance is expected, whether it is late, and the next step: ask its status now, wait until a day, the next questions after a not found, or retries for a payer whose system did not answer. Your own follow-up codes come back as you sent them, never read. Nothing is stored, nothing is sent, and no payer is asked.

Remittances, denials and appeals

  • POST/v1/remittances/explain

    Explain a remittance

    An 835 claim by claim: what was paid, allowed and written off, the patient’s share, the verdict in words, each service line, every adjustment and remark code in words, and whether each check adds up to the claims it pays.

  • POST/v1/denials/next-step

    The next step on a denial

    An 835, or the claims as JSON: for each claim and each adjustment, what the payer did, whether the patient may be billed, the next step, the corrected claim fields to send, what to attach, and every deadline with where it comes from: a Medicare regulation with its section, the limits you send, or no date at all.

  • POST/v1/appeals/letter

    Write an appeal letter

    An appeal letter written from the facts you send, by a fixed template for its kind. It never adds a code, an amount or a date you did not give. A fact the kind needs and you left out is named, and nothing is written.

  • POST/v1/appeals/letter/check

    Check an edited letter

    A letter you edited, held to the facts by the same rule: every code, amount and date it states, however it is written (a dollar sign or the word dollars, a written or numeric date, CO-50 or CARC 50, N115 or N 115), must be one the facts hold, and a placeholder left in is found.

  • POST/v1/remittances/pdf

    Print a remittance

    An 835 laid out the way Medicare’s Standard Paper Remittance is read: each check, each claim and its service lines with what was billed, allowed, applied to the deductible and coinsurance, adjusted and paid, the check’s totals and provider-level adjustments, and a glossary of every code on the file. Up to 500 claims and 3,000 service lines a call. The answer is your PDF as base64.

  • POST/v1/appeals/form

    Fill a Medicare appeal form

    CMS’s own redetermination request (CMS-20027) or reconsideration request (CMS-20033), the file CMS publishes, filled from the claim and the 835 that denied it: the beneficiary, the Medicare number, the codes of the lines the payer denied (a line it paid is never listed) and their dates of service, the notice date, and why you disagree in your words. Original Medicare only. Nothing is signed, dated or chosen for you, the deadline comes with its regulation, and the form is yours to review and send.

  • POST/v1/remittances/from-image

    Read a paper EOB into a draft 835

    A photo, scan or PDF of a payer’s paper explanation of benefits (two pages at most, 1.4 MB), read by a model into a DRAFT: every figure, date and code with the reader’s confidence and where on the page it was read, the payments checked by arithmetic against the check total the page states, and an 835 written as a test file. It is never an ERA and nothing is posted: check every field against the paper. Off until Payerworks turns it on; test keys and invented documents only for now, at most 100 reads a UTC day for an account’s keys together; a call refused for the route being off, a live key or no file is not counted. A read has 25 seconds; past them the answer is 504 model_read_timed_out: send one page, or a crop of the part you need. Nothing is kept.

  • POST/v1/remittances/duplicates

    Find duplicate remittances

    Send several 835s and get back every check that is one payment sent more than once: the same trace number (TRN02) from the same originator (TRN03), never the trace alone. Each duplicate says whether the copies are the same remittance or differ in content, and a check with no trace is listed apart. Nothing is stored.

  • POST/v1/remittances/reassociate

    Match deposits to remittances

    Send 835s and the deposits from your bank (the amount and effective date, and the trace number or description when your bank gives them). Each deposit is matched to one check by its trace number first, then by a trace in the description, then by amount and date only when exactly one check and one deposit fit, and every match says what agreed and what did not. Unmatched deposits and checks are listed. No bank description is repeated back.

  • POST/v1/remittances/split

    Split an 835 by payee or rendering provider

    Send an 835 paid to one tax ID and get one file per payee (the provider the payer grouped the claims under) or per rendering NPI. Each file’s payment total is recomputed from its own claims and provider-level adjustments and read back to check it balances, the parts add up to the original check, and each keeps its trace. An adjustment that names no single group gets a file of its own, never a guessed one, and a value holding a delimiter is refused, never rewritten. Send the interchange control number you track for the first file (each further file takes the next): the original’s own number is never reused. The files are yours to load; nothing is sent.

  • POST/v1/remittances/recoupments

    Match recoupments to the claims they take back

    Send a set of 835s. Every provider-level adjustment is named, and each takeback is matched to the paid claim its reference names in the files you sent: by payer claim number, then patient control number, then amount when only one earlier payment at the same payer fits. Unmatched takebacks are listed, with what each payer took back and what the files show still carried forward. It says what was taken and from which claim, never whether it should have been.

  • GET/v1/filing-limits

    Published claim filing windows

    The claim filing window each payer in Payerworks’ table publishes, read on its own pages on 09/10/2026, and Medicare fee-for-service’s from 42 CFR 424.44. Search with q, and add date_of_service to count the last day where the payer states one figure; a payer whose pages leave the window to its agreement says so, with no day. Your agreement with the payer governs over a published window, and nothing here is a payer’s answer about a claim.

  • GET/v1/filing-limits/{payer}

    One payer’s filing window

    One payer by its slug from GET /v1/filing-limits, or by its name, with every plan line it publishes a window for; add date_of_service to count the last day. A payer with no window on record is 404 no_filing_limit_on_record, never a window.

Analytics from your own files

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

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

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

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

Prior authorization

  • POST/v1/authorizations/278

    Write a 278

    Up to one hundred prior authorization or referral requests, from JSON (the kind of review, the service type, each procedure code with its units and dates, the requester, the servicing provider, the patient and the diagnoses), written as one 005010X217 interchange for you to send on your own connection, or built in the shape of the Da Vinci PAS request bundle with output set to fhir. Each request is checked field by field first, and nothing is written if any would be refused. A test key writes test files only. Whether a payer takes a 278 at all is the payer’s choice: many take prior authorizations only on a portal, by fax or by phone.

  • POST/v1/authorizations/explain

    Explain a 278 response

    A payer’s 278 response, as X12, as Payerworks’ own JSON read of one, or as a PAS response, said per review: approved, approved in part, approved with changes, not approved, pended, waiting on more information, canceled, no review needed, or not reviewed at all, with the reason in words, the certification and reference numbers, the approved dates and units against what you asked, the claim field the number goes in, and the next step. A request the payer never reviewed is never called a denial.

  • POST/v1/sandbox/authorizations

    Run the prior authorization sandbox

    The round trip on invented data: your requests written as a 278 and the sandbox payer’s 278 response, chosen by how each member ID ends, read back and explained in the same call. Any key may call it, every file is a test file, and nothing reaches a payer.

  • POST/v1/authorizations/required

    Does Medicare require it

    Up to one hundred procedure codes checked against CMS’s published Medicare fee-for-service lists on a date of service: the DMEPOS Required Prior Authorization List and the hospital outpatient department list, each named and dated, with the state or the setting asked for where the list depends on it. A code on neither list is said as not on the lists held, never as not required, and for any other payer the answer is that no published list is held.

Patient estimates and money

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

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

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

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

Hospital standard charges

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

Payer directory

  • GET/v1/payers

    Search the payer directory

    Payers and plan programs from public federal files (Medicare Advantage and Part D contracts, Medicaid managed care plans, insurers’ loss ratio filings, employer plans’ Form 5500 carriers) and the Medicare contractors; not a list Payerworks can send to; a payer ID only where the payer publishes it, with the page and the day it was read. Search with q by name, a misspelling, a payer ID, a NAIC code or a CMS contract ID (a state named in q is read as state=); narrow with state and type; page with limit (up to 100) and starting_after. Every row says connection none, and an all-digit payer ID under five digits is warned about: leading zeros are part of it.

  • GET/v1/payers/{id}

    One directory row

    One row in full by its directory id (pwd_ and 16 hex): its names, where its sources say it serves (or Not stated), programs, the payer IDs it publishes with their sources, and the files it came from. A payer ID sent here is refused 400 not_a_directory_row_id and pointed to the search, never answered as not found.

  • POST/v1/payers/from-card

    Look up a payer from a card photo

    A photo of an insurance card’s front (and back, if you have it), read twice by a model for the payer’s name, the member ID and its shape, the group number and the plan type the card itself states, and the payer looked up in the Payerworks payer directory. A match means the directory holds published facts about that payer, never that a claim can be sent to it; an electronic payer ID printed on the card is shown only where the payer publishes it. Off until Payerworks turns it on; test keys and invented cards only for now, at most 100 reads a UTC day for an account’s keys together; a call refused for the route being off, a live key or no file is not counted. A read has 25 seconds; past them the answer is 504 model_read_timed_out: send one page, or a crop of the part you need. Nothing is kept.

Providers and enrollment

  • POST/v1/providers

    Create a provider

    A provider record: its name, billing NPI, tax id (an EIN, never a Social Security number) and contacts. One per NPI and tax id pair: a second is refused, naming the first. With a live key the answer also says what the NPI registry holds for the NPI: whether it is there, Type 1 or 2, and whether the provider’s name matches (its own name only, never a contact’s; a warning, never a refusal; a lookup that fails says so, and is never read as not found).

  • GET/v1/providers

    List your providers

    Newest first. Page with limit (up to 100) and starting_after, and filter with npi, tax_id or q (part of the name).

  • GET/v1/providers/{id}

    One provider

    The provider, and whether its NPI and tax id are locked because a request for it has gone to a payer.

  • POST/v1/providers/{id}

    Change a provider

    Only the fields you send change. The NPI and tax id can be corrected until a request for the provider has gone to a payer.

  • DELETE/v1/providers/{id}

    Delete a provider

    Its drafts go with it. A provider with a request that is open or live is kept, and the refusal names those requests. Deleting again answers the same.

  • POST/v1/enrollments

    Request an enrollment

    One request per provider, payer and transaction: eligibility, claim_status, professional_claims or remittances. Kept as a draft, or handed to Payerworks with submit: true. Payerworks is not yet registered with any payer, so a live request waits with Payerworks, the answer carries a route_not_open warning, and nothing is sent.

  • GET/v1/enrollments

    List your enrollments

    Newest first, each with its status, whose turn it is and its open tasks. Filter with status, transaction, payer_id, provider_id, npi, waiting_on and the created and updated dates. A page may hold fewer than limit, so keep paging while has_more is true.

  • GET/v1/enrollments/{id}

    One enrollment

    Its status and whose turn it is, in words; its tasks in rank order, its documents, notes from Payerworks, a rejection with its remedy, and the history of every status change.

  • POST/v1/enrollments/{id}

    Change an enrollment

    The contact, the notice email, the effective date, the remittance grouping, the PTAN or your note, until the request is sent to a payer; while it is a draft, the payer and the transaction too.

  • DELETE/v1/enrollments/{id}

    Delete an enrollment

    A draft, a canceled or a rejected request. Deleting again answers the same.

  • POST/v1/enrollments/{id}/submit

    Submit a draft

    The draft is checked again and handed to Payerworks.

  • POST/v1/enrollments/{id}/cancel

    Cancel an enrollment

    In any open status. Before it is sent to a payer it is canceled at once; once it is with a payer, or live, the answer is 202 and Payerworks withdraws it.

  • POST/v1/tasks/{id}

    Answer a task

    Save a task’s values, text or an uploaded document, and finish it with complete: true, in rank order. A task held up by an earlier one is refused naming that task, its rank and who holds it, Payerworks included. complete: false reopens a task finished by mistake while nothing has gone to a payer.

  • POST/v1/enrollments/{id}/documents

    Upload a PDF for a task

    A signed upload address for one PDF of up to 10 MB, good for 15 minutes. Post the file there, then confirm it, or finish the task with its document_id, which confirms it too.

  • POST/v1/documents/{id}/confirm

    Confirm an upload

    Checks that the file arrived and is a PDF of up to 10 MB. One that is not is deleted, and the document is marked failed.

  • GET/v1/documents/{id}

    Download a document

    A signed download address, good for 15 minutes, for a PDF you uploaded or a form Payerworks attached to a task.

  • DELETE/v1/documents/{id}

    Delete a document

    Refused while a finished task names it: reopen the task first. Deleting again answers the same.

  • GET/v1/enrollment-requirements

    Payers with published steps

    Every payer Payerworks holds published enrollment steps for, the four Medicare DME MACs and CMS’s HETS, and the sandbox payer: per transaction, who files, the fields needed and each step, with its source and the day it was read.

  • GET/v1/enrollment-requirements/{payer_id}

    What a payer asks

    For any payer id, per transaction: whether Payerworks’ route is open, who files, the fields needed and each published step with its source. A payer Payerworks holds nothing for answers published: false, never not required. Add ?transaction= for one.

  • POST/v1/sandbox/enrollments/{id}/advance

    Advance a sandbox enrollment

    The sandbox payer’s next move on a test request, with the events it raised. A test key only; the last word of the provider’s name picks the scenario.

  • POST/v1/enrollments/import/check

    Check an enrollment import

    Send { csv } with the template’s header and up to 100 rows, one enrollment request a row. Each row is checked exactly as POST /v1/providers and POST /v1/enrollments check their bodies, each problem named by its column, and said as ready, invalid or duplicate (a request that already stands, or an earlier row asking the same). With a live key, each NPI is looked up in the NPI registry. Nothing is saved.

  • POST/v1/enrollments/import

    Import enrollment requests

    The same file, each valid row made into one request, and its provider when your key has none with that NPI and tax id. A provider you already have is used as it is, never changed. Each row answers created, invalid, duplicate, refused or not_run; rows not started in time are not_run from not_run_from_row, so send those again. A row is submitted only when its submit cell says true.

  • GET/v1/enrollments/export

    Export your enrollments

    Every request matching the list’s filters, as CSV text in the answer, up to 5,000 rows: status, whose turn, provider, payer, transaction and the dates. No contact, note or full tax id. A cell a spreadsheet would run as a formula starts with an apostrophe. It takes no limit or starting_after.

  • GET/v1/enrollments/durations

    How long your requests took

    By payer and transaction, how long your own requests spent waiting on Payerworks, on the provider and with the payer, and from submitted to live: how many finished, and once at least five did, the median and the middle half in hours. Fewer than five says not enough history. From your key’s own history only: Payerworks has no other figures.

Codes in words

  • GET/v1/codes/carc/{code}

    An adjustment reason code

    A claim adjustment reason code in words, with its category, sent bare (45) or with its group (CO-45). A remark code sent here is named as one.

  • GET/v1/codes/group/{code}

    An adjustment group code

    A claim adjustment group code (CO, PR, OA, PI or CR) in words, and whether the patient may be billed an amount under it.

  • GET/v1/codes/claim-status/{category}

    A claim status category

    A claim status category code in words, read both ways: on a claim acknowledgment (277CA), whether it means accepted, rejected or denied, and in a claim status answer, the state the explainer gives it.

  • GET/v1/codes/claim-status-code/{code}

    A claim status code

    A claim status code in words, and whether it names an entity.

  • GET/v1/codes/service-type/{code}

    A service type code

    A service type code in words: its 005010 meaning where Payerworks has verified it, and the meaning later X12 versions gave a code they reassigned.

  • GET/v1/codes/aaa/{code}

    A reject reason code

    A reject reason from a 271 (its AAA segment) in words, whether asking again can help, and the request fields to correct. Add ?loop=2100D for a dependent.

Sandbox payers

  • POST/v1/sandbox/claims

    Run the sandbox payer

    Your claims written as an 837P and answered by the sandbox payer in the same call: a 999 for the file, a 277CA for each claim and an 835 for each claim it adjudicated, every file explained, and claims[] with each claim’s warnings, its repairs and the checks that could not run. Any key may call it, every file is a test file, and nothing reaches a payer. How the member ID ends decides the answer. A member ID ending in RFAI is held for a document: a 277 request for additional information follows. Send the same claims again with the solicited 275 (attachments_275, at most five) and the claim is paid.

  • POST/v1/sandbox/claims/837i

    Run the sandbox payer on an 837I

    Your institutional claims checked as POST /v1/claims/837i checks them, written as an 837I and answered by the same sandbox payer as the professional claims, by the same rules: how the member ID ends decides the answer, and the paid amounts and payer claim numbers are made the same way. The answer is a 999 for the file, a 277CA for each claim and an 835 for each claim it adjudicated, and explain_with names the routes that explain them. A member ID ending in RFAI is held for a document, as on POST /v1/sandbox/claims: send the same claims again with the solicited 275 in attachments_275 and the claim is paid. Any key may call it, every file is a test file, nothing is posted to your webhook, and nothing reaches a payer.

  • POST/v1/sandbox/claims/837d

    Run the sandbox payer on an 837D

    Your dental claims and predeterminations, checked as POST /v1/claims/837d checks them and answered by the same sandbox payer, by the same member ID endings, with a 999, a 277CA and an 835. A predetermination is answered as one: CLP02 25, nothing paid, each line’s would-be payment as an OA 101 adjustment, and the estimate in the outcome. A member ID ending in RFAI is held for a document: send the same claims again with the solicited 275 in attachments_275 and the claim is paid. Any key may call it, every file is a test file, nothing is posted to your webhook, and nothing reaches a payer.

  • POST/v1/sandbox/eligibility

    Run the eligibility sandbox

    Your eligibility questions written as a 270 and answered by the sandbox payer with a 271 in the same call, every answer explained. Any key may call it, every file is a test file, and how the member ID ends decides the answer.

  • POST/v1/sandbox/claim-status

    Run the claim status sandbox

    Your claim status questions, as JSON or a 276, answered by the sandbox payer with a 277 in the same call and explained. It agrees with the claims sandbox: the same payer claim number and, for each claim, the same paid amount. How the member ID ends decides the answer.

  • POST/v1/sandbox/claims/timeline

    Run the sandbox over days

    The claims sandbox spread over simulated time: the 999 after five minutes, a 277CA saying each claim was received after an hour, the payer’s own 277CA after two days and the 835 after fourteen. Send the claims, a seed, the start and the moment to read as of; send a later moment and the cursor you were given to read what came next. Each event is the exact body a webhook sends, so you can post it to your own handler; Payerworks posts none of them. Add format: report for the 277CAs and 835 in the JSON field names most clearinghouse APIs use. Nothing is stored or sent: the same request gives the same answer.

Your keys, your account and your webhook

  • GET/v1/health

    Health

    Whether the API is answering. No key needed.

  • GET/v1/account

    Your account

    The key’s account, label, mode and rate, the calls it made on each day of this month (counted by UTC day) and the calls refused before they ran, and its webhook if one is set, with what its last delivery did.

  • PUT/v1/account/webhook

    Set your webhook

    The https address Payerworks posts your events to. The first answer carries the signing secret, once. Setting the address again keeps the secret; send rotate: true for a new one, and the previous secret keeps signing beside it for overlap_hours (0 to 720, 24 by default).

  • DELETE/v1/account/webhook

    Remove your webhook

    No more events are sent. Removing a webhook that is not there is not an error.

  • POST/v1/account/webhook/test

    Send a test event

    One ping event queued for your webhook, signed like every other. The answer is 202 with the event’s id; GET /v1/events/{id} says a moment later what your server did with it.

  • GET/v1/account/contact

    Your notice address

    Where Payerworks emails you about this key: when one of its webhook destinations keeps failing, and when one of its enrollment requests moves without your doing it. A test key from this page starts with the address its link was sent to, the only address it may set.

  • PUT/v1/account/contact

    Set your notice address

    The address told when a destination has failed for 2 hours, for 24 hours, and when it was disabled after its last attempt, and sent one email for every enrollment request that moved without your doing it inside the same 15 minutes. Send email: null to remove it, and nothing is emailed. Only the address that asked for this account’s test key by email (any key of the account may set it), or null; any other address is refused 403 contact_not_proved. A key issued by Payerworks has its address set by us: write to us.

  • GET/v1/keys

    Your account’s keys

    Every key of your account in your key’s own mode (a test key lists the test keys, a live key the live ones): its name, mode, last four characters, when it was made, when it expires, when it was last rotated, until when its previous secret is accepted, whether it has expired, when it was revoked, and which one is the key asking, beside the account’s id. A revoked key is listed too. Never a key itself.

  • POST/v1/keys

    Make a key

    A named key of your key’s own mode and rate, shown once. expires_in_days (1 to 365) makes it stop on its own. A key never outlives the key that made it: its expiry is the sooner of expires_in_days and the making key’s own, and the making key’s when none is asked. An account holds 3 test keys or 25 live ones that are not revoked. Every key of an account may manage its keys, unless the account’s organization chose members only.

  • DELETE/v1/keys/{id}

    Revoke a key

    The key stops working within a minute, and its SFTP user, if it has one, is removed at once. Revoking a key that is already revoked is not an error.

  • POST/v1/keys/{id}/rotate

    Rotate a key

    A new secret for the same key id, shown once, so its webhooks, events, usage and enrollments carry on. The old secret is still accepted for overlap_hours, 0 to 720, 24 by default, and stops at once if the key is revoked. Two rotations at once: the first finishes, and the other answers 409 key_rotation_conflict and makes nothing.

  • POST/v1/mcp

    The MCP server, hosted

    The same tools as the downloadable MCP server, over MCP’s Streamable HTTP transport (2026-07-28, and the 2025 handshakes), with your key as the bearer. Each tool call is a call to its route under your key. With a live key no tool is called unless the request carries Payerworks-MCP-Allow-Live-Key: 1. Payerworks transmits nothing.

  • GET/v1/audit-log

    Your account’s audit log

    Every change to this account, newest first: keys made, rotated, revoked or expired; the webhook, destinations and the notice address; providers, enrollment requests, tasks and documents; the SFTP user and its SSH keys; batch jobs; the organization and its members. Each entry says what, when, who (a key, a member, Payerworks, or Payerworks automatically), through which door, the object’s id, and the fields that changed by name only, never a value; a web address by its host. limit 1 to 100 (25), type (an entry type, or its family before the dot), created_gte and created_lt, starting_after. Each entry is kept 365 days, then deleted, and never changed before then. Every key of the account reads the same log.

  • GET/v1/audit-log/export

    Download the audit log

    The same entries and filters as a CSV file in the answer (csv, filename, columns), at most 5,000 entries a call; next_cursor names where the rest start. No cell opens as a formula in a spreadsheet.

Events

  • GET/v1/events

    List your events

    Every webhook event queued for your key that is still kept (each is kept 30 days), newest first, each as it was sent with what delivering it did. Page with limit (up to 100) and starting_after (the next_cursor of the page before, or an event id), and filter with type, delivery_state (none: recorded for your webhook destinations only, with no account webhook set) and created_gte and created_lt.

  • GET/v1/events/{id}

    One event

    One event by its id, as it was sent, with its delivery: pending, delivered or failed, how many times it was posted, the last status and the last error.

  • POST/v1/events/{id}/redeliver

    Send an event again

    The event queued once more for the address your webhook has now, signed the same way and with the same id. The answer is 202, and the event’s delivery then says what your server did. It counts against your key’s six deliveries a minute. Send destination_id to send it to that webhook destination instead, as a manual attempt that starts its retries again from the first.

  • GET/v1/events/{id}/attempts

    Every attempt for an event

    Each post made for one event at your webhook destinations: its number, automatic or manual, the status your server answered and when. destination_id narrows it to one.

  • GET/v1/webhook-destinations

    List your webhook destinations

    Every destination your key posts events to, besides your account webhook: up to ten, each with its name, address, the event types it takes, whether it is enabled, how many deliveries it takes at once, and the last four characters of its secret.

  • POST/v1/webhook-destinations

    Add a webhook destination

    A named https address with its own signing secret, shown once in this answer. event_types narrows what it receives (leave it out for every type), enabled turns it off and on, and concurrency (1 to 10, 2 by default) is how many deliveries it takes at once. Only events Payerworks makes are sent. A delivery that fails is tried again 5 minutes, 30 minutes, 2 hours, 8 hours, 24 hours and 48 hours later; after the seventh attempt fails, the destination is disabled.

  • GET/v1/webhook-destinations/{id}

    One webhook destination

    One destination, with when it started failing and the notices sent about it. Never its secret.

  • PUT/v1/webhook-destinations/{id}

    Change a webhook destination

    Change any of its fields, turn it on again after it was disabled, or send rotate: true for a new secret (shown once); the old one keeps signing beside it for overlap_hours, 0 to 720, 24 by default.

  • DELETE/v1/webhook-destinations/{id}

    Remove a webhook destination

    No more events are sent to it, and its retries already scheduled are skipped. Removing one that is not there is not an error.

  • POST/v1/webhook-destinations/{id}/ping

    Ping a webhook destination

    One signed ping event to this destination alone, whatever event types it takes. The answer is 202 with the event’s id.

  • GET/v1/webhook-destinations/{id}/deliveries

    What a destination received

    Each event’s delivery at this destination, newest first: pending, scheduled (failed, with the next automatic attempt’s time), delivered or failed, how many attempts, the last status and error. Filter with state, created_gte and created_lt; page with limit and starting_after.

SFTP file drop

  • GET/v1/sftp

    Your SFTP user

    The host, your user name, your two folders (/inbound and /outbound), your SSH keys by fingerprint, when a 999 is written, and what is kept and for how long. A key with no user answers 404 sftp_user_not_found.

  • PUT/v1/sftp

    Make your SFTP user

    Makes this key’s SFTP user, or changes when a 999 is written for a file you drop: acknowledgment_999 is always, errors_only (the default: only when a group or a set is not plainly accepted) or never. A test key’s user sends usage indicator T to receiver PAYERWORKSTEST; a live key’s sends P to PAYERWORKS (qualifier ZZ). Payerworks validates and acknowledges what you drop; nothing goes on to a payer.

  • DELETE/v1/sftp

    Remove your SFTP user

    The user can no longer sign in. Answers already in /outbound are deleted by the bucket 7 days after each was written.

  • POST/v1/sftp/ssh-keys

    Add an SSH key

    One OpenSSH public key line (ssh-ed25519, ssh-rsa or ecdsa), as your .pub file holds it; at most 10. There are no passwords. A private key is refused and never kept; a key’s comment is dropped.

  • DELETE/v1/sftp/ssh-keys/{id}

    Remove an SSH key

    That key can no longer sign in.

Batch jobs

  • POST/v1/jobs

    Make a batch job

    For more than one call can carry: name the route (route, as POST /v1/claims/validate) and, if every call carries one, the query. You get the job and a signed upload form for its file: { calls: [ ... ] }, each entry exactly the body that route takes in one call, up to 1,000 calls and 50 MB, uploaded within an hour. Each call is answered as the route answers it and held to its limits (2 MB a call; a larger one is answered 413 too_large at its index). A job runs claim validation, 837P writing, remittance, eligibility and claim status explanations, analytics, payment variance and claim tracking. POST /v1/eligibility/270 as a job is different: its file is that route’s own body with up to 7,500 requests, written as one 270. A file whose requests would write more segments than one 270 is read in fails the job job_270_too_many_segments. A job keeps your file until it finishes and its result for 24 hours, which no other route does: use invented data with a test key. Off until Payerworks turns it on; test keys only for now; 50 jobs a UTC day for an account’s keys together. Nothing is sent to a payer.

  • GET/v1/jobs

    Your batch jobs

    This key’s jobs, newest first, at most 100: each one’s route, state and counts. A job whose result’s 24 hours are over is listed as expired, with nothing kept, until its record is removed.

  • GET/v1/jobs/{id}

    One batch job

    Its state (awaiting_upload, running, completed, partial, failed, canceled or expired), its calls by status, and once it has finished, a link to its result good for 15 minutes; ask again for a new one. The result is { answers: [ { index, status, body } ] }, each answer what that call would have answered. A job that ran out of time is partial: the answered calls are in the result and not_run_from is the first to send again. A job that failed says why in error. A job.completed or job.failed event reaches your webhook and destinations when it ends. A job whose file arrived but could not be started on our side within an hour fails with job_not_started.

  • POST/v1/jobs/{id}/cancel

    Cancel a batch job

    Before its file is uploaded, nothing will run. While it runs, it stops between two calls and keeps no result. A finished job cannot be canceled: delete it.

  • DELETE/v1/jobs/{id}

    Delete a batch job

    Its file, its result and the job itself, now rather than after 24 hours. A running job must be canceled first.

Organizations and members

  • GET/v1/org/roles

    The roles

    The one role table: every role, every permission and the owner’s rules. Readable with a key or a sign-in.

  • GET/v1/org/memberships

    Your organizations

    The organizations this sign-in belongs to, with its role in each. A sign-in only.

  • POST/v1/org

    Make an organization

    Make an organization for an account: name it and give one key of the account (api_key). The key proves the account and is checked and not kept; you become its owner. An account belongs to one organization. A sign-in only.

  • GET/v1/org

    Your organization

    The organization, with your role and what it lets you do. Name it in the Payerworks-Organization header when your sign-in belongs to several. A sign-in only.

  • PUT/v1/org

    Change your organization

    Rename it, or choose who manages the account’s keys (key_management): keys_and_members, the default, where an API key of the account may still make, rotate and revoke its keys, or members_only, where only a signed-in member with the role for it may. An owner’s.

  • GET/v1/org/members

    The members

    Every member: the email address of the sign-in, the name, the role, when they joined and who invited them. A sign-in only.

  • PUT/v1/org/members/{id}

    Change a member’s role

    A new role: owner, admin, developer or viewer. Only an owner makes, changes or demotes an owner, and the last owner is never demoted.

  • DELETE/v1/org/members/{id}

    Remove a member

    The member loses access at once. revoke_keys=true, an owner’s choice, also revokes the keys they made or rotated in the console (each one whose secret they were shown); left out, those keys keep working and are listed in the answer. The last owner is never removed, and a member may remove themselves to leave.

  • GET/v1/org/invitations

    The invitations waiting

    Every invitation not yet accepted or canceled: the address, the role, who invited and until when the link works. A sign-in only.

  • POST/v1/org/invitations

    Invite a person

    Invite a person by email with a role. Payerworks emails a link that works for 7 days, and only for a sign-in at that verified address; the email names neither the organization nor who invited. An organization sends at most 25 invitations a UTC day.

  • DELETE/v1/org/invitations/{id}

    Cancel an invitation

    The link stops working at once.

  • POST/v1/org/join

    Accept an invitation

    Accept an invitation with the token from its link (the part after #join=). The sign-in’s verified email address must be the address the invitation was sent to. A sign-in only.

  • GET/v1/org/keys

    The account’s keys

    Every key of the organization’s account, test and live, as GET /v1/keys lists them, with who made each: a member in the console, or a key. Never a key itself. A sign-in only.

How claims and files are read

Claims are JSON in the 837P’s own structure: submitter, receiver, billing, subscriber (and dependent when the patient is not the subscriber), rendering and referring providers, and claimInformation with its patientControlNumber, claimChargeAmount, placeOfServiceCode, claimFilingCode, healthCareCodeInformation and serviceLines. patientControlNumber is your own reference, at most twenty characters; every answer about the claim carries it back. An institutional (837I) or dental (837D) claim takes the same shape, with institutionalService or dentalService on its lines and the fields its own route names.

curl "$PAYERWORKS_BASE_URL/v1/claims/validate" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d @claim.json
  • A file’s kind is read from its functional group and its implementation guide, so a 277CA claim acknowledgment is never read as a claim status 277. A kind Payerworks recognizes and does not read yet is named, never called unreadable.
  • One kind of file a call: an interchange whose functional groups are of different kinds is refused, naming them, so no part of it goes unread.
  • Every answer fits in one response; one that would not is refused 413 answer_too_large, so split the file.
  • A path value with a broken percent escape is refused 400 invalid_path. A query parameter may be at most 200 characters, and no query is ever logged.

Retries

Safe to retry

Send an Idempotency-Key header with any POST, and with the webhook PUT, and a retry gets the first answer instead of a second run.

  • Use a new key for each new request: a UUID is a good one. A key is remembered for 24 hours.
  • A retry with the same key and the same request gets the first answer again, with the header idempotent-replayed: true. What the first request did is not done a second time, and the sandbox’s webhook events keep their ids, so your server can ignore one it already handled.
  • If the first answer said its webhook events could not be recorded or queued, and to retry, the retry with the same key does that step instead of repeating the refusal.
  • The same key with a different request is refused with 422. While the first request is still running, a retry is refused with 409 and retry-after.
  • An answer that failed on our side (a 5xx) is not remembered, so a retry runs the request again.
  • Payerworks keeps a fingerprint of the request and the moment it ran, so it can answer the same way again. It never keeps the request or the answer.
key=$(uuidgen)   # one new key per request; reuse it only to retry that request
curl "$PAYERWORKS_BASE_URL/v1/sandbox/claims" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $key" \
  -d @claims.json

Every answer carries an x-request-id header naming that request, and every refusal carries request_id inside the error: the same id, except on an Idempotency-Key replay, where request_id names the request that first answered and x-request-id names the retry. Quote either when you write to us.

Answers pushed to you

Webhooks

Set an https address in the console and every answer the 837P claims sandbox (POST /v1/sandbox/claims) makes, and every change to an enrollment request, is posted to it as an event, the moment it is made, signed with a secret only you and Payerworks hold. An event your server does not take is tried again, and every event is kept for 30 days, to list and to send again.

  • ping

    Sent when you ask for a test event, from the console or POST /v1/account/webhook/test. It carries no claim.

  • sandbox.file.acknowledged

    The 999 for your file: its verdict, how many transaction sets were received and accepted, and the control numbers of the interchange, the group and each set.

  • sandbox.claim.acknowledged

    One for each claim, from the 277CA: your reference, the payer claim number, accepted or rejected, and every fix as a sentence.

  • sandbox.remittance.received

    One for each check in the 835, once the payer adjudicated anything (an 835 pays each billing provider by its own check): the check number, its date and total, and each claim that check paid, with its reference, status and amount paid.

  • enrollment.updated

    An enrollment request changed: a status change no type below names, or its fields, tasks, documents or notes. It carries the status, the previous status, whose turn it is, the reason, the fields that changed and the sequence.

  • enrollment.live

    The payer confirmed the request: files of this kind may flow. It adds when, and the payer’s reference.

  • enrollment.rejected

    The request was refused. It adds the rejection: the payer’s words whole, and the remedy.

  • enrollment.canceled

    The request was canceled, or you asked to cancel one that is with the payer or live (cancel_requested says which).

  • enrollment.ended

    A live route ended: the payer ended it, or a cancel you asked for while it was live went through (in the sandbox, at the next advance).

  • enrollment.task.assigned

    A task was added: its id, rank, who holds it, its title and its fields.

  • enrollment.task.completed

    A task was finished, by the provider, or by Payerworks for its own.

  • enrollment.task.deleted

    A task was replaced by another (superseded_by names it).

  • job.completed

    A batch job finished, every call answered or, when it ran out of time, partly: its id, route, state and counts, and until when its result is kept. Never the result or a link to it: ask GET /v1/jobs/{id} for the link.

  • job.failed

    A batch job failed, or was stopped: its id, route, state, counts and the failure’s code. Nothing of the file you uploaded.

  • submission.file.acknowledged

    The partner’s 999 for a file you sent with POST /v1/claims/submit: accepted, rejected or partial, with each refused claim’s fix in words.

  • submission.claim.acknowledged

    The partner’s 277CA verdict on one claim you sent, with the payer’s claim number when it gives one, and each fix in words.

  • submission.remittance.received

    An 835 whose every claim you sent: the check, and each claim’s billed, paid and patient share. An 835 that also pays claims you did not send is held, and reaches no one.

Every event is a POST of JSON in one shape. In it, created is the time in Unix seconds, mode is the key’s (a sandbox event is always test), and id is the same each time the same event is sent, a retry and a resend included, so you can ignore one you already handled.

POST /your/webhook
Content-Type: application/json
User-Agent: Payerworks-Webhooks/1
webhook-id: evt_8a1c…
webhook-timestamp: 1792324800
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
Payerworks-Signature: t=1792324800,v1=5f0c…

{ "id": "evt_8a1c…", "type": "sandbox.claim.acknowledged", "created": 1792324800, "mode": "test",
  "data": { "reference": "PW-SBX-REJ", "verdict": "R", "fixes": [ "The claim was rejected…" ] } }

Each POST is signed under the Standard Webhooks specification, so any of its libraries checks it: webhook-id is the event’s id, webhook-timestamp the time it was signed in Unix seconds, and webhook-signature is v1, a comma and the signature in base64.

That signature is HMAC-SHA256 over the id, a period, the timestamp, a period and the raw body exactly as it arrived, keyed with your signing secret’s bytes: the part after whsec_, decoded from base64.

Each POST also carries the Payerworks-Signature header the first receivers were built on: t, the time it was signed in Unix seconds, and v1, HMAC-SHA256 keyed with your whole signing secret over the time, a period and the raw body, written in hex.

Check one of them before you trust the body: compute the same HMAC, compare the two in constant time, and refuse a time more than five minutes from your clock, so a captured event cannot be sent to you again.

Checking the Standard Webhooks signature, in three languages:

import crypto from "node:crypto";

// headers: the headers of the request (Node gives their names in lowercase);
// rawBody: the request body exactly as it arrived, before any JSON parsing.
export function verifyWebhook(secret, headers, rawBody, toleranceSeconds = 300) {
  const id = headers["webhook-id"];
  const timestamp = String(headers["webhook-timestamp"] || "");
  if (!id || !/^\d+$/.test(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > toleranceSeconds) return false;
  const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
  const expected = crypto.createHmac("sha256", key).update(`${id}.${timestamp}.`).update(rawBody).digest();
  return String(headers["webhook-signature"] || "").split(" ").some((entry) => {
    const [version, signature] = entry.split(",");
    const given = Buffer.from(signature || "", "base64");
    return version === "v1" && given.length === expected.length && crypto.timingSafeEqual(given, expected);
  });
}

Checking the Payerworks-Signature header instead, for a receiver built on it:

import crypto from "node:crypto";

// rawBody: the request body exactly as it arrived, before any JSON parsing.
export function verifyPayerworks(secret, header, rawBody, toleranceSeconds = 300) {
  const fields = { v1: [] };
  for (const part of String(header || "").split(",")) {
    const at = part.indexOf("=");
    if (at < 1) continue;
    const name = part.slice(0, at).trim();
    const value = part.slice(at + 1).trim();
    if (name === "v1") fields.v1.push(value); else fields[name] = value;
  }
  const t = fields.t || "";
  if (!/^\d+$/.test(t) || Math.abs(Date.now() / 1000 - Number(t)) > toleranceSeconds) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${t}.`).update(rawBody).digest();
  return fields.v1.some((v) => {
    const given = Buffer.from(v, "hex");
    return given.length === expected.length && crypto.timingSafeEqual(given, expected);
  });
}
  • Events are queued when the call is answered and posted a moment later, so your call never waits on your server. The call’s answer says how many were queued. A key may queue six deliveries a minute.
  • Answer with any 2xx within four seconds and the event is delivered. A redirect, a timeout or any other answer is not, and Payerworks tries again: twice more, one and then two minutes later, sending only the events not yet delivered. All of one try’s events share ten seconds, and an endpoint that could not be reached is not asked again in that try.
  • An event can arrive more than once, so ignore one whose webhook-id you already handled.
  • Your address must be https, at a hostname rather than an IP address, on the standard port, with no user name or password in it.
  • The signing secret begins whsec_ and is shown once, when it is made (and again to a retry of that same request with the same Idempotency-Key, within 24 hours). Setting the address again keeps it. Ask for a new one with rotate: true; the previous secret then signs every event beside the new one for 24 hours, or the overlap_hours you send (0 to 720), so a header carries two signatures and your server can switch over without missing one.
  • A secret made before 10/19/2026 is written in an older alphabet (base64url, without padding). It keeps working, and the samples below read both; rotate it for one every Standard Webhooks library reads as it is.

Setting the address, rotating its secret and sending a test event from your own system:

# Set the address. The first answer carries the signing secret once: store it.
curl -X PUT "$PAYERWORKS_BASE_URL/v1/account/webhook" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{ "url": "https://example.com/payerworks/events" }'

# Rotate the secret: a new one, shown once; the old one signs beside it for 24 hours.
curl -X PUT "$PAYERWORKS_BASE_URL/v1/account/webhook" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com/payerworks/events", "rotate": true }'

# Queue one ping event for it (202, with the event id).
curl -X POST "$PAYERWORKS_BASE_URL/v1/account/webhook/test" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY"

# A moment later: what the delivery did, as webhook.last_delivery.
curl "$PAYERWORKS_BASE_URL/v1/account" -H "Authorization: Bearer $PAYERWORKS_API_KEY"

Webhook destinations

  • Besides the one account webhook, a key may hold up to ten webhook destinations, each with its own name, https address and signing secret (shown once), the event types it takes and how many deliveries it takes at once (1 to 10, 2 by default). Only events Payerworks makes are sent, and a ping always reaches the destination it is sent to.
  • A delivery a destination did not take is tried again 5 minutes, 30 minutes, 2 hours, 8 hours, 24 hours and 48 hours after the attempt before it, seven attempts in all. After the seventh fails, the destination is disabled; PUT /v1/webhook-destinations/{id} with enabled: true turns it on again.
  • Each post to a destination carries Payerworks-Attempt-Number, Payerworks-Attempt-Type (automatic, or manual for a redelivery you asked for) and Payerworks-Destination-Id beside both signatures. GET /v1/events/{id}/attempts lists every attempt, and a destination’s deliveries say which are pending, scheduled, delivered or failed.
  • Set a notice address with PUT /v1/account/contact, and Payerworks emails it when a destination has been failing for 2 hours, for 24 hours, and when it is disabled. Each notice goes with the first failed attempt past its mark, so the first comes with the attempt about 2 hours 35 minutes after the first failure. A notice names the destination by its id and its address’s host, never by its name.
  • POST /v1/events/{id}/redeliver with destination_id sends an event to that destination as a manual attempt and starts its retries again from the first. A destination’s secret is rotated the way the account webhook’s is, with overlap_hours.

The event log

  • GET /v1/events lists your key’s events newest first, each as it was sent with what delivering it did: pending, delivered or failed, how many times it was posted, the last status (null when your server gave no answer) and the last error. An event that could not be queued, whose webhook was removed before it went, or that no delivery reported on within 65 minutes of being queued is failed too, its last error saying why and how to send it again. Pass limit (up to 100), type, and starting_after (the next_cursor of the page before, or an event id) to read further.
  • GET /v1/events/{id} reads one event, and POST /v1/events/{id}/redeliver sends it again to the address set now. An event not yet delivered that is queued again, this way or by a retry with the same Idempotency-Key, reads pending until that delivery is done.
  • An event is kept for 30 days and holds only what the event says: the sandbox payer’s invented answer, your claim references and your file’s control numbers, never the claim you sent; an enrollment event holds ids, statuses and words, never a tax id, a contact, a note or a document.
  • An account’s keys together record up to 2,000 events a UTC day. Past that, a run still answers in full, but its events are not recorded or sent, and the answer says so; events are recorded again from 00:00 UTC (retry the run with the same Idempotency-Key then to record and send them).
# The newest ten events, then the page after them.
curl "$PAYERWORKS_BASE_URL/v1/events?limit=10" -H "Authorization: Bearer $PAYERWORKS_API_KEY"
curl "$PAYERWORKS_BASE_URL/v1/events?limit=10&starting_after=$NEXT_CURSOR" -H "Authorization: Bearer $PAYERWORKS_API_KEY"

# Only one type, and one event by its id.
curl "$PAYERWORKS_BASE_URL/v1/events?type=sandbox.remittance.received" -H "Authorization: Bearer $PAYERWORKS_API_KEY"
curl "$PAYERWORKS_BASE_URL/v1/events/$EVENT_ID" -H "Authorization: Bearer $PAYERWORKS_API_KEY"

# Send it again to the address set now (202).
curl -X POST "$PAYERWORKS_BASE_URL/v1/events/$EVENT_ID/redeliver" -H "Authorization: Bearer $PAYERWORKS_API_KEY"

Your key’s rate, its calls by day and your webhook are in the console. Open the console.

Download and build

Client libraries and an MCP server

Three files, generated from the same route list as this page and openapi.json, so every route here is a method there. Each is plain source with no dependencies: download it, read it, and keep it in your own repository.

  • JavaScript

    An ES module for Node 18 or later, on your server: one method per route, typed in JSDoc, and uploadDocument for an enrollment task’s PDF. Your key is a bearer key, so never put it in browser code.

    /sdk/payerworks.mjs
  • Python

    For Python 3.9 or later, the standard library only: one method per route.

    /sdk/payerworks.py
  • MCP server

    For an AI agent: run it with node over stdio, and each route that answers a question is a tool, with the reads of your account and your enrollment requests, except the routes that answer with a PDF, the two that read a picture with a model, and the enrollment export. Setting, removing or testing your webhook, sending an event again, and every change to a provider, an enrollment request, a task, a document, a key or a destination stay with you. One tool writes to your account: a sandbox claims run records its events and posts them to your webhook, when one is set.

    /sdk/payerworks-mcp.mjs
  • The JavaScript and Python clients each send an Idempotency-Key on every POST and PUT, and retry a 429, a 503 or a 409 idempotency_in_flight at most twice, after the wait retry-after names, with the same key. A refusal is a PayerworksError carrying the status, the code, the sentence and the request id.
  • The two clients also each check a webhook’s signature, in the Standard Webhooks headers or the Payerworks-Signature header, in constant time.
  • The MCP server makes its calls the same way: an Idempotency-Key on every POST its tools send, and the same retries with the same key, waiting at most 30 seconds. A refusal reaches the model as a tool error carrying the status, the code, the sentence and the request id.
  • Both clients can keep your own request log: requestLog: true (request_log=True in Python) keeps the last 1,000 attempts in your process, a number keeps that many, and requestLogFile (request_log_file) appends one JSON line an attempt to a file of yours. Each entry is the method, the path, the status, the request id, the time it took and the Idempotency-Key, never a body, a query or a header. Payerworks keeps no such record of your calls.
  • Each of the three calls only the base URL you give it, never follows a redirect, and sends nothing anywhere else. The one exception is the JavaScript client’s uploadDocument, which posts your PDF, without your key, to the https upload address the API answered for it. Download again when this page lists a new route: each file names the build it was made from.

Add the MCP server to your AI client’s configuration:

// Download /sdk/payerworks.mjs from this site into your project.
import { Payerworks, PayerworksError } from "./payerworks.mjs";

const pw = new Payerworks({ apiKey: process.env.PAYERWORKS_API_KEY, baseUrl: process.env.PAYERWORKS_BASE_URL });
try {
  const { valid, claims } = await pw.postClaimsValidate({ claim });
  const page = await pw.getEvents({ query: { limit: 10 } });
} catch (e) {
  if (e instanceof PayerworksError) console.log(e.status, e.code, e.message, e.requestId);
}

With a test key, send invented data only. With a live key, the AI model and its provider read the patient data each tool sends and receives, so the server refuses a live key unless you set PAYERWORKS_MCP_ALLOW_LIVE_KEY=1. Set it only where that is permitted, for example under a business associate agreement that covers your AI client.

The same tools are also served by the API itself: POST /v1/mcp, over MCP’s Streamable HTTP transport (revision 2026-07-28, and the 2025 handshakes), with your key as the bearer and nothing to download. Each tool call is a call to its route under your key, metered and rate-limited as that route. With a live key no tool is called unless the request carries Payerworks-MCP-Allow-Live-Key: 1.

Keys and limits

  • Send your key on every call as `Authorization: Bearer <key>`.
  • A test key (pw_test_…) is yours from the form on this page, for invented data only. It writes test interchanges and runs the sandbox payer, and nothing sent with it reaches a payer.
  • A live key (pw_live_…) writes production files. Payerworks issues it once a business associate agreement is signed; ask from the form at the foot of the page.
  • Each key has a rate in calls a minute: a test key from this page has 60, and a key Payerworks issues has 120 unless another rate is agreed. The rate is counted in a window that resets at the start of each UTC minute. Every answer to a route called with a valid key, an error included, carries x-ratelimit-limit, x-ratelimit-remaining and x-ratelimit-reset (the seconds until the window resets), and the same figures in the RateLimit and RateLimit-Policy fields of the IETF draft. Past the limit the answer is 429 with retry-after. An answer given before a key is read carries none of them: a missing or invalid key, an unknown path (404), a method the route does not answer (405), a path that does not decode (400), or GET /v1/health, which takes no key.
  • A call the API turns away before any work is done is counted as refused, apart from your calls: over the rate, a body that is not JSON or is too large, a query it cannot read, or an Idempotency-Key it cannot read, already used for another request or still running.
  • A revoked key stops working within a minute.
  • An account is the keys one key started. POST /v1/keys makes a named key of your key’s own mode and rate, shown once, with an expiry if you want one (1 to 365 days); a key never outlives the key that made it: its expiry is the sooner of expires_in_days and the making key’s own, and the making key’s when none is asked. An account holds 3 test keys or 25 live ones that are not revoked. Every key of an account may list its keys, and make, rotate and revoke them unless the account’s organization chose that only its signed-in members do.
  • POST /v1/keys/{id}/rotate gives a key a new secret under the same key id, so its webhooks, events, usage and enrollments carry on. The old secret is still accepted for overlap_hours (24 by default, up to 720) and stops at once if the key is revoked; a second rotation ends the first one’s overlap. Two rotations at once: the first finishes, and the other answers 409 key_rotation_conflict and makes nothing.

Errors

Every refusal has one shape:

{ "error": { "code": "invalid_claims", "message": "One or more claims would be refused; nothing was written.", "claims": [ … ] } }
  • 400 The request is malformed: no claim, bad JSON, a bad id, a path or a query that does not decode, or an Idempotency-Key that cannot be read.
  • 401 No key, or a key that is not valid; on an organization route, a key where a member’s console sign-in is needed, or a sign-in that has ended.
  • 403 The key may not do this, such as a test key asking for a production file, a live key on a route open to test keys only, or a route not open on this deployment yet (sftp_not_open); or a member whose role does not allow the call (org_role_forbidden).
  • 404 No such route, or no such event for this key.
  • 405 The route does not answer that method; the allow header names the ones it does.
  • 409 Not now: a test event before a webhook address is set, a request with the same Idempotency-Key still running (retry after the seconds retry-after names), a webhook or a destination that changed while your request ran, or a limit reached (ten destinations, an account’s keys, ten SSH keys).
  • 410 Gone: an invitation’s link is used within 7 days, and this one is older. Ask a member of the organization for a new one.
  • 413 The body is over the size limit, or the answer would be larger than one response carries (answer_too_large): split the file, or send fewer claims or questions a call.
  • 422 The input was read and would be refused (every claim’s or question’s errors are listed, and nothing was written), an interchange mixes kinds of file, or an Idempotency-Key was already used for a different request.
  • 429 Over the key’s rate (wait the seconds retry-after names), over its webhook deliveries a minute, or past the events its account records in a UTC day, or past its account’s model reads a day (a test event is refused until 00:00 UTC).
  • 500 Something failed on our side. Retry; nothing you sent was kept.
  • 501 This route is not offered where you asked, and asking again answers the same: only the hosted API answers it, because it reads your key’s account (answered_by_the_door), a model read is switched off on this deployment (model_read_not_offered), or the sandbox timeline is not connected here (timeline_not_offered); organization sign-in is not open here (org_not_offered); or a claim cannot be sent to a payer yet (submit_not_offered, with each of its gates). Neither client retries a 501.
  • 502 A model read gave no usable answer for the file you sent (model_read_failed): send a clearer copy, or try again.
  • 503 We could not read or write your key’s records, or queue its events, just now; the message says what was not done. Retry in a moment; both clients retry a 503 twice by themselves. The same when the part of the API that draws a PDF or reads a picture could not be loaded (documents_unavailable, model_read_unavailable).
  • 504 A model read did not finish inside one call (model_read_timed_out): send one page, or a crop of the part you need.

Every error code

Every refusal’s error.code is one of these: stable, snake_case, and always said with the same status. The list is read from the engine’s own catalog. A refusal that lists problems field by field names each one’s own code and field inside it.

All 347 codes
no_claim
400
The body has no claim: send `claim` (or `claims`) in the 837P shape the docs describe.
too_many_claims
400
At most 100 claims a call.
invalid_claims
422
One or more claims would be refused; nothing was written or run. Each claim's errors are listed.
duplicate_reference
422
Two claims carry the same reference (patientControlNumber, CLM01); each must be unique so its answers match back.
no_x12
400
Send the file's text as `x12`.
not_read_here
422
This file is a kind Payerworks recognizes but does not read here yet; the error names its kind.
unrecognized_x12
422
This is not an X12 file Payerworks reads; the message lists the kinds it reads.
not_an_835
422
This is not an 835 remittance.
not_an_acknowledgment
422
This is not a 999, a 277CA or a TA1.
invalid_code
400
An adjustment reason code is one to four digits, or a letter and digits (such as 45, A1 or B13), optionally after its group.
invalid_category
400
A claim status category is a letter and up to two letters or digits, such as A1 or F2.
invalid_path
400
The path holds a percent escape that does not decode (for example %E0 or a lone %). Encode the value with encodeURIComponent.
invalid_sandbox_attachments
422
A 275 sent back to the sandbox cannot be read; nothing was run. Each problem is listed with its field.
answered_by_the_door
501
This route reads your key's account, so only the hosted API answers it, never the engine run on its own (in a browser or a test).
invalid_sender_id
400
sender_id is your submitter id at the receiver: 2 to 15 letters, digits or spaces.
invalid_receiver_id
400
receiver_id is the receiver's interchange id: 2 to 15 letters, digits or spaces.
invalid_control_number
400
control_number is the interchange control number you track, a whole number from 1 to 999999999.
invalid_usage
400
usage is "T" (test) or "P" (production).
test_key_production_file
403
A test key writes test interchanges only (ISA15 = T). Use a live key for a production file.
sandbox_test_only
400
A sandbox payer answers test interchanges only: usage is "T" or left out.
invalid_query
400
A query parameter's name or value is over 200 characters, or holds a percent escape that does not decode.
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.
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.
no_request
400
The body has no eligibility request: send `requests` (a list), `request`, or the request itself in the JSON shape the docs describe.
too_many_requests
400
At most 100 eligibility requests a call.
invalid_requests
422
One or more eligibility requests would be refused; nothing was written. Each request's errors are listed.
duplicate_trace
422
Two requests in one file carry the same trace (controlNumber); each must be unique so its answer matches back.
no_answer
400
Send the 271 as `x12` (the file's text) or as `json` (the JSON answer).
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.
invalid_asked
400
asked is { service_type_codes: [codes], date_of_service: YYYYMMDD }, each optional.
invalid_service_type
400
A service type code is one to three letters or digits, such as 30 or 98.
invalid_aaa_code
400
A reject reason code (AAA03) is one or two letters or digits, such as 72 or T4.
invalid_loop
400
loop is the loop the reason was returned in, such as 2100C or 2110D.
invalid_include
400
include names what to add to the answer: answer (the normalized read of the 271).
invalid_shape
400
shape names how the answer that include=answer adds is written: normalized (the default) or plans (the grouped JSON shape).
no_inquiry
400
Send the claim status question as `inquiry` (or `inquiries`), in the JSON claim status shape.
too_many_inquiries
400
At most 100 claim status questions a call.
invalid_inquiries
422
One or more claim status questions would be refused; nothing was written. Each question's errors are listed.
duplicate_inquiry_trace
422
Two questions in one file carry the same trace (encounter.trackingNumber); each must be unique so its answer matches back.
no_claim_status_answer
400
Send the payer's claim status answer as `x12` (a 277) or `json` (the JSON answer).
ambiguous_answer
400
Send the answer once: as `x12` or as `json`, not both.
invalid_claim_status_json
400
`json` is the JSON claim status answer, an object with its `claims` list (or its errors).
not_a_claim_status_answer
422
This is not a claim status answer Payerworks reads: send a 277 (005010X212) or the JSON answer.
invalid_status_asked
400
`asked` describes the question you asked; one of its fields is not in the form it takes.
not_a_claim_status_question
422
This is not a claim status question: send a 276 (005010X212) as `x12`, or the questions as `inquiry` or `inquiries`.
invalid_status_code
400
A claim status code is one to five digits, such as 65 or 689.
ambiguous_question
400
Send the claim status questions once: as `inquiry` or `inquiries`, or as `x12` (a 276), not both.
invalid_payer_name
400
`payer_name` is the payer's name as the 276 writes it (2100A NM103): text of at most 60 characters.
invalid_submitter_name
400
`submitter_name` is your organization's name as the 276 writes it (2100B NM103): text of at most 60 characters.
no_remittance
400
Send an 835 as x12, or the claims as a list in claims.
two_inputs
400
Send either x12 (an 835) or claims (a list), not both.
invalid_claim_facts
422
One or more claims or facts are not usable; nothing was decided. Each problem is listed with its field.
invalid_letter_facts
422
One or more facts are not usable; nothing was written. Each is listed with its field.
unknown_letter_kind
400
kind is one of the letter kinds the answer lists, with the facts each requires.
missing_facts
422
The letter needs facts that were not sent; the answer lists them.
letter_refused
422
The letter was not written: a fact holds a placeholder in brackets, which the guard refuses.
invalid_letter
400
Send the letter's text as letter, at most 20000 characters.
invalid_group_code
400
A claim adjustment group code is two letters, such as CO or PR.
is_a_remark_code
400
This is a remittance advice remark code (RARC), not a claim adjustment reason code (CARC).
invalid_limit
400
limit is a whole number from 1 to 100.
invalid_cursor
400
starting_after is the next_cursor of an earlier page, or the id of one of this key's events.
invalid_event_type
400
type is one of the event types the developers page lists.
invalid_event_id
400
An event id is evt_ followed by 16 to 64 lowercase hexadecimal characters.
event_not_found
404
No event of this key has that id. Events are kept for 30 days.
invalid_delivery_state
400
The delivery state filter names one of the states the developers page lists.
invalid_created
400
created_gte and created_lt are instants written like 2026-10-19T14:00:00Z, and created_gte comes before created_lt.
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).
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.
invalid_tracked_claims
422
One or more claims cannot be tracked; nothing was read. Each problem is listed with its field.
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.
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_import
422
The CSV could not be read as an enrollment import, so no row was checked or saved. Each problem is listed; the columns are in the import template.
export_takes_no_paging
400
An export holds every request that matches its filters, so it takes no limit or starting_after.
export_too_large
413
Every request matching these filters does not fit in one export. Narrow the filters (a payer, a transaction, a created range) and export each part.
invalid_provider
422
One or more provider fields would be refused; nothing was saved. Each problem is listed with its field.
provider_exists
409
This key already has a provider with this NPI and tax id; the error names it.
provider_limit_reached
409
A key holds at most 500 providers (a test key 50). Delete one you no longer use.
invalid_provider_id
400
A provider id is prv_ followed by 24 lowercase hexadecimal characters.
provider_not_found
404
This key has no provider with that id.
provider_in_use
409
The NPI and tax id can no longer change: a request for this provider has gone to a payer. The error names those requests.
provider_has_enrollments
409
This provider has requests that are open or live; cancel them first. The error names them.
invalid_starting_after
400
starting_after is the next_cursor of an earlier page, or the id of an item on an earlier page.
invalid_npi_filter
400
npi is the provider’s 10-digit NPI, with a valid check digit.
invalid_tax_id_filter
400
tax_id is the provider’s 9-digit EIN, with or without the hyphen after the second digit.
q_too_long
400
q is at most 100 characters.
invalid_enrollment
422
One or more enrollment fields would be refused; nothing was saved. Each problem is listed with its field.
enrollment_exists
409
This provider already has an open request for this payer and transaction; the error names it and its status.
enrollment_limit_reached
409
A key holds at most 5,000 enrollment requests (a test key 500). Delete drafts and ended requests you no longer need.
invalid_enrollment_id
400
An enrollment id is enr_ followed by 24 lowercase hexadecimal characters.
enrollment_not_found
404
This key has no enrollment request with that id.
enrollment_sent
409
This request has gone to the payer, so its fields can no longer change. Cancel it and file a new one.
enrollment_ended
409
This request is canceled, rejected or ended; nothing more can change. File a new request.
enrollment_not_deletable
409
Only a draft, canceled or rejected request can be deleted. Cancel it first.
enrollment_not_draft
409
Only a draft is submitted; this request already was.
invalid_cancel
422
reason is text of at most 200 characters, with no run of eight or more digits.
invalid_status_filter
400
status is one or more of the eight enrollment statuses, separated by commas.
invalid_transaction_filter
400
transaction is eligibility, claim_status, professional_claims or remittances.
invalid_payer_filter
400
payer_id is 1 to 80 letters and digits.
invalid_provider_filter
400
provider_id is prv_ followed by 24 lowercase hexadecimal characters.
invalid_waiting_on_filter
400
waiting_on is you, provider, payerworks or payer.
invalid_date_filter
400
A date filter is a day, YYYY-MM-DD, or an instant, YYYY-MM-DDTHH:MM:SSZ.
invalid_task_id
400
A task id is tsk_, the enrollment’s 24 hexadecimal characters, an underscore and two digits.
task_not_found
404
This key has no task with that id.
task_not_open
409
This request is not waiting on the provider, so its tasks cannot change now.
task_blocked
409
A task ranked before this one is still open; the error names it, its rank and who holds it. Tasks are finished in rank order.
task_is_payerworks
409
This task is Payerworks’ to do, not the provider’s. It is shown so you can see what Payerworks is waiting on.
invalid_task_values
422
One or more task values would be refused; nothing was saved. Each problem is listed with its field.
invalid_document_request
422
Send task_id, field_key and filename (ending .pdf, at most 120 characters); each problem is listed with its field.
field_not_a_document
422
That field of the task does not take a document.
document_limit_reached
409
An enrollment request holds at most 20 documents. Delete one you no longer need.
upload_unavailable
503
No upload address could be made just now; nothing was saved. Retry in a moment.
invalid_document_id
400
A document id is doc_, the enrollment’s 24 hexadecimal characters, an underscore and 8 more.
document_not_found
404
This key has no document with that id.
upload_missing
409
Nothing has arrived at the upload address yet. Upload the PDF, then confirm again.
not_a_pdf
422
The uploaded file is not a PDF (it does not start %PDF-). It was deleted; upload the PDF again.
document_too_large
422
A document is at most 10 MB. It was deleted; upload a smaller PDF.
document_not_uploaded
409
This document has not been uploaded and confirmed, so there is nothing to download.
document_in_use
409
A completed task names this document. Reopen the task first (complete: false), then delete it.
invalid_payer_id
400
A payer id is 1 to 80 letters and digits.
sandbox_nothing_to_advance
409
The sandbox payer has no move to make now: the next move is yours, the request has ended, or this scenario never moves. The error says whose turn it is.
changed_meanwhile
409
This request changed while yours ran, so nothing was saved. Send it again.
invalid_edit_filter
400
A filter on GET /v1/edits is snip_type (1 to 7), severity (error or warning), kind (deterministic or code_set) or runs_in (edits, validateClaim, validateSecondaryClaim or correction-plan); the message names the one that is not.
invalid_edit_code
400
An edit code is snake_case letters, digits and underscores, at most 64 characters, such as future_date.
edit_not_found
404
No edit has this code: GET /v1/edits lists every edit Payerworks runs and the ones it does not.
invalid_change
400
change is "replace" (correct a claim) or "void" (cancel it).
invalid_payer_claim_number
400
payer_claim_number is the payer's own number for the claim, at most 50 characters and none of the X12 delimiters (~ * : ^).
payer_claim_number_needed
422
The payer processed the claim, so the plan needs its claim number: it is CLP07 on the 835, REF*1K on the 277CA, or the number a claim status answer gives.
invalid_medicare_mac
400
medicare_mac is true or false.
invalid_adjudicated
400
adjudicated is true or false: true says the payer processed the claim even when its number is not at hand.
claim_type_not_planned
422
claim_type is "professional" or "dental": an institutional claim carries its frequency in the type of bill, which this plan does not cover yet.
invalid_correction_claim
400
original and replacement are each one claim in the 837P JSON shape POST /v1/claims/837p takes.
not_an_837p
422
An x12 read as claims (original_x12 and replacement_x12 on POST /v1/claims/correction-plan, x12 on POST /v1/claims/validate) is an 837P interchange (GS08 005010X222A1).
several_claims_in_file
422
original_x12 and replacement_x12 each hold exactly one claim: split a bulk file with POST /v1/x12/parse first.
adjudication_unknown
422
Say whether the payer processed the claim: send payer_claim_number (CLP07 on the 835, REF*1K on the 277CA) when it did, or adjudicated: false when it was rejected before processing (a 999 or 277CA rejection). Sending a processed claim again as a new one is a duplicate.
not_an_837d
422
With claim_type dental, original_x12 and replacement_x12 are each a one-claim 837D interchange (GS08 005010X224A2).
dental_not_checked_here
422
A dental claim sent as an 837D is read where the API runs (POST /v1/claims/correction-plan), not on this page: send it as JSON here, or call the API.
payer_not_in_directory
404
The Payerworks directory holds no published facts about this payer. It lists payers and plan programs from public federal files, not every payer, and not the payers Payerworks can send to.
not_a_directory_row_id
400
GET /v1/payers/{id} takes a directory row id (pwd_ and 16 hex), never a payer ID: search GET /v1/payers?q= to find a payer by its payer ID or name.
invalid_payer_query
400
A search parameter is out of shape; the error names it in field (q, state, type, limit or starting_after).
payer_directory_unavailable
503
The payer directory is not loaded where this was asked; the Payerworks API answers it.
no_claims_to_follow
400
Send the open claims as `claims` (each with its reference, payer_id and sent_on), or the files as `files` (x12_837, acknowledgments, remittances).
ambiguous_follow_up
400
Send the claims once: as `claims` or as `files`, not both.
invalid_follow_up_request
400
A field of the request is not in the shape it takes; each problem is listed with its field.
too_much_to_follow
413
More was sent than one call takes: the error names what was counted (`unit`) and the most one call takes (`limit`).
no_remittances
400
Send the 835s as remittances: a list of X12 files, each the file's text.
too_many_remittances
413
More was sent than one call reads across its files (at most 50 files, 1000 checks, 5000 claims, 5000 provider-level adjustments and 2000 deposits): the error names the unit and the limit. Send fewer in each call.
deposits_required
400
Send the bank's deposits as deposits: a list of { amount, effective_date, trace_number?, originator?, description?, id? }.
invalid_deposits
422
One or more deposits are not usable; nothing was matched. Each problem is listed with its field.
invalid_split_by
400
by is "payee" (the 2000 loop's provider, TS301) or "rendering_npi" (each claim's rendering provider, NM1*82).
value_holds_a_delimiter
422
A value in the file holds one of the characters Payerworks writes as a delimiter (* : ^ ~), so it cannot be copied unchanged; nothing was written.
invalid_payer
400
Name the payer by its slug from GET /v1/filing-limits, or by its name.
invalid_date_of_service
400
date_of_service is a day, YYYY-MM-DD.
no_filing_limit_on_record
404
No published filing window is on record for this payer. Ask the payer, or read your agreement with it; GET /v1/filing-limits lists every payer that has one.
invalid_analytics_request
400
A field of the request is not in the shape this route takes; each problem is listed with its field.
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.
nothing_to_summarize
400
Send the claims (claims or x12_837) with their acknowledgments and remittances, or the remittances alone.
invalid_fee_schedule
422
One or more fee schedule rows are not usable; nothing was judged. 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.
invalid_risk_claim
422
The claim cannot be compared: each problem is listed with its field. Nothing was read.
invalid_outcomes
422
One or more appeal outcomes are not usable; nothing was ranked. Each problem is listed with its field.
no_identity_request
400
Send `request` (the eligibility request the payer refused, in the JSON POST /v1/eligibility/270 takes) and the reasons: `reasons` [{ code, loop }] or the refusing `answer` (x12 or json).
invalid_identity_request
422
The request would be refused as a 270 before any retry is planned: its errors are listed.
invalid_reasons
400
reasons is a list of { code, loop }: code an AAA reject reason (such as 75), loop where it was returned (such as 2100C), at most 20.
invalid_known
400
known holds only text facts the caller gives: legal_first_name, previous_last_name, dependent_member_id, each at most 80 characters.
no_reasons
422
Neither reasons nor the answer names a reject reason to plan from.
no_answer_to_read
400
Send the 271 as `x12` (the file’s text) or as `json` (either JSON eligibility answer).
answer_not_read
422
An answer could not be read as an eligibility answer: send an X12 271 with its envelope, or a JSON eligibility answer.
invalid_service_type_test
400
Send { baseline, specific, service_type_code, baseline_service_type }: two answers (x12 or json), the specific service type code asked, and the baseline asked (30, or 35 for dental).
invalid_coordination
422
The coordination question would not be answered as sent: each problem is listed.
invalid_discovery_verify
400
Send { patient: { first_name, last_name, date_of_birth, address?, previous_addresses?, gender? }, coverages: [answers, x12 or json], date_of_service? }.
too_many_coverages
400
Verify at most 10 coverages a call.
no_authorization_request
400
The body has no review request: send `requests` (a list), `request`, or the request itself in the JSON shape the docs describe.
too_many_authorization_requests
400
At most 100 review requests a call.
invalid_authorization_requests
422
One or more review requests would be refused; nothing was written. Each request's errors are listed.
duplicate_authorization_trace
422
Two review requests in one file carry the same trace (controlNumber); each must be unique so its response matches back.
invalid_output
400
output is "x12" (the default), "fhir" (the Da Vinci PAS bundle shape) or "both".
no_authorization_answer
400
Send the 278 response as `x12` (the file's text), as `json` (Payerworks' own read of one) or as `fhir` (a Da Vinci PAS response Bundle or ClaimResponse).
not_a_278
422
This is not an X12 278 request for review and response (005010X217): send the 278 with its ISA envelope.
not_a_278_response
422
This 278 holds requests only (BHT02 13): send the payer's response (BHT02 11).
not_a_278_json
422
This JSON is not a read 278: it holds no `reviews` with a decision or a rejection.
not_a_pas_response
422
This FHIR resource is not a PAS response: send a Bundle holding a ClaimResponse, or the ClaimResponse, with a reviewAction or an error.
invalid_asked_requests
400
asked is the review request, or the list of them, that the response answers.
invalid_explain_include
400
include names what to add to the answer: answer (the read of the 278).
no_procedure_codes
400
Send `procedure_codes`: a list of HCPCS or CPT codes, such as L1833 or 64490.
too_many_procedure_codes
400
At most 100 procedure codes a call.
invalid_procedure_codes
400
Each procedure code is a HCPCS Level II code (a letter and four digits) or a five-character CPT code.
invalid_payer_type
400
payer_type is medicare_fee_for_service, medicare_advantage, medicaid, commercial or other.
invalid_state
400
state is the two-letter state the patient lives in, such as CA.
invalid_setting
400
setting is hospital_outpatient or other (where the service is furnished), or left out.
no_estimate_lines
400
Send lines: the planned services, each with the allowed amount you expect (allowed_amount), at most 50 a call.
too_many_estimate_lines
400
At most 50 estimate lines a call.
invalid_estimate
422
The estimate request has fields that cannot be read; nothing was figured. Each problem is listed.
member_required
422
This 271 answers more than one patient: send member_index to say which one to estimate.
no_member
422
This 271 answers no patient to estimate.
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.
no_dme_items
400
Send items: each { hcpcs, delivered_on }, at most 100 a call.
too_many_dme_items
400
At most 100 items a call.
invalid_dme_items
422
One or more items cannot be read; nothing was answered. Each problem is listed.
invalid_fpl_request
422
The request has fields that cannot be read; nothing was figured. Each problem is listed.
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_document_option
400
An option of this document is not one the route takes: the error names it and what it takes.
too_many_forms
400
These claims need more CMS-1500 forms than one call draws (200, at six service lines a form): send fewer claims in each call.
no_remittance_file
400
Send the 835 as x12 (the file's text).
invalid_attachments
422
The 275 cannot be written as asked; nothing was written. Each problem is listed with its field.
documents_unavailable
503
The PDF could not be drawn just now: the part of the API that draws it is not loaded. Retry in a moment. The engine run on its own, in a browser or a test, draws no PDF; the hosted Payerworks API, at the base URL shown with your key, does.
unknown_appeal_form
400
form is redetermination (CMS-20027) or reconsideration (CMS-20033).
invalid_appeal_form_input
422
One or more fields cannot be typed into the form; nothing was filled. Each problem is listed with its field.
claim_not_on_remittance
422
The 835 holds no claim payment with this reference (CLP01); send the reference the denying claim payment carries.
not_medicare_fee_for_service
422
These are Original Medicare's forms, and the 835 says this claim is not Medicare fee-for-service (CLP06 is not MA or MB): use the plan's own appeal, or POST /v1/appeals/letter.
form_asset_changed
503
The form file on our side is not the one CMS published that we pinned, so nothing was filled. This is ours to fix.
no_image
400
Send the file as base64: file_base64 for a paper EOB, front_base64 (and back_base64 if you have it) for an insurance card.
invalid_image_base64
400
The file is not base64: send the file's bytes base64 encoded (A-Z, a-z, 0-9, + and /, with = padding), with no data: prefix.
image_too_large
413
The file is larger than one call reads: at most 1,400,000 bytes in all, before base64. Send a smaller copy (a lower resolution photo, or fewer pages).
unreadable_image
422
The file could not be read as a picture or a PDF; the error says why (reason) and what to send instead.
too_many_pages
413
The PDF holds more pages than one call reads: the error names the most (limit). Send fewer pages in each call.
model_read_not_offered
501
Reading a document with a model is not offered on this host: Payerworks turns it on route by route. This is not a passing fault, so sending the request again answers the same.
model_read_test_keys_only
403
This route answers test keys only, with invented documents, until an agreement covering the model is in place for live data. Use a test key.
model_read_unavailable
503
The document reader could not be loaded just now, so nothing was read. Retry in a moment. The engine run on its own, in a browser or a test, has no reader; the hosted Payerworks API does.
model_read_timed_out
504
The read did not finish inside one call's time. Send less: one page, or a crop of the part you need.
model_read_failed
502
The reader gave no usable answer for this file, so nothing is answered from it. Send a clearer copy, or try again.
not_an_eob
422
The reader did not see a payer's explanation of benefits or remittance advice in this file, so no draft was written.
not_an_insurance_card
422
The reader did not see a health insurance card in this file, so nothing was looked up.
model_read_daily_limit
429
This account's keys have made the most model-read calls an account may make on this route today (100 a UTC day, its keys together). Try again tomorrow.
invalid_seed
400
seed is your own string for the run, 1 to 64 of A-Z, a-z, 0-9, _ and -: the same seed, claims and start give the same run.
invalid_instant
400
start and as_of are moments written in ISO 8601 with a time and a zone (such as 2026-10-19T09:00:00Z), from the year 2000 to 2099, and as_of is not before start.
invalid_timeline_cursor
400
cursor is the next_cursor an earlier timeline answer gave, unchanged.
cursor_other_run
400
This cursor belongs to another timeline run: the claims, the seed or the start differ from the run that made it.
invalid_timeline_limit
400
limit is a whole number from 1 to 500.
invalid_format
400
format is "x12", "report" or "both".
timeline_not_offered
501
The sandbox timeline is not offered where this was asked: it is not connected to the claim checks here, so nothing was run. This is not a passing fault, so sending the request again answers the same.
no_rules
400
Send the rules as `rules`, a list of one or more rule objects.
too_many_rules
400
At most 100 rules a call.
invalid_rules
422
One or more rules are not written in the rule format; nothing was applied. Each rule's problems are listed.
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).
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.
mrf_not_json
422
`json` is not a JSON document that can be read.
invalid_row_offset
400
row_offset is a whole number from 0 to 1000000000: the rows of the whole file before the first data row of this piece.
invalid_destination_id
400
A destination id is wd_ followed by 16 lowercase hexadecimal characters.
destination_not_found
404
This key has no webhook destination with that id.
destination_limit_reached
409
A key holds at most 10 webhook destinations. Remove one to add another.
invalid_destination_name
400
A destination's name is 1 to 80 characters.
invalid_event_types
400
event_types is a list of the event types the developers page lists; an empty list, or leaving it out, receives every type.
invalid_enabled
400
enabled is true or false, or left out.
invalid_concurrency
400
concurrency is a whole number from 1 to 10: how many deliveries this destination takes at once.
invalid_overlap_hours
400
overlap_hours is a whole number from 0 to 720: how long the old secret is still accepted after a rotation.
destination_disabled
409
This destination is disabled. Enable it with PUT /v1/webhook-destinations/{id} and `enabled: true` first.
destination_conflict
409
The destination changed while this request ran, so nothing was saved. Send the request again.
not_at_destination
404
That event was never sent to this destination (it was made before the destination was added, or its type is filtered out). A ping or a new event will be.
invalid_contact_email
400
email is an address of at most 254 characters, or null to remove it.
contact_not_proved
403
Notices go only to an address Payerworks has proved: the one that asked for this account's test key by email and opened the link we sent. Send that address, or null to stop the notices. To use another, write to us from it.
invalid_key_id
400
A key id is pwk_ followed by 16 to 40 letters and digits.
key_not_found
404
This account has no key with that id.
key_limit_reached
409
An account holds at most 3 test keys or 25 live keys that are not revoked. Revoke one to make another.
invalid_key_name
400
A key's name is 1 to 80 characters.
invalid_expires_in_days
400
expires_in_days is a whole number from 1 to 365, or left out for a key that does not expire.
key_revoked
409
That key is revoked, so it cannot be rotated. Make a new key with POST /v1/keys.
key_rotation_conflict
409
Another rotation of this key finished while this one ran, so this one made nothing: the secret that other request showed is the key's current one. List the keys with GET /v1/keys, and rotate again if that secret was lost.
key_settings_out_of_range
409
The key's stored rate or account name is outside what a key may hold, so nothing was made or rotated; reason says which. Ask Payerworks to correct the key: sending the request again answers the same.
sftp_not_open
403
The SFTP file drop is not open on this Payerworks deployment. Every API route still answers.
sftp_user_not_found
404
This key has no SFTP user. Make one with PUT /v1/sftp.
invalid_acknowledgment_999
400
acknowledgment_999 is "always", "errors_only", "never": when a 999 is written for a file you drop.
unknown_sftp_field
400
PUT /v1/sftp takes acknowledgment_999 and nothing else. An SFTP user signs in with an SSH key only: add one with POST /v1/sftp/ssh-keys.
invalid_ssh_public_key
400
public_key is one OpenSSH public key line (ssh-ed25519, ssh-rsa, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521), as the .pub file holds it.
private_key_sent
400
That is a PRIVATE key. Nothing was kept: send the matching public key (the .pub file), and keep the private key on your own machine.
ssh_key_limit_reached
409
An SFTP user holds at most 10 SSH keys. Remove one to add another.
ssh_key_exists
409
This SFTP user already holds that SSH key.
ssh_key_refused
422
The SFTP endpoint refused that SSH key, so nothing was added. Send another public key; an ssh-ed25519 key is the simplest.
invalid_ssh_key_id
400
An SSH key id is key- followed by 17 lowercase hexadecimal characters, as GET /v1/sftp lists it.
ssh_key_not_found
404
This SFTP user holds no SSH key with that id.
sftp_unavailable
503
The SFTP user could not be read or changed just now, so nothing changed. Send the request again.
jobs_not_offered
501
Batch jobs are not offered on this host: Payerworks turns them on when they are ready. This is not a passing fault, so sending the request again answers the same.
jobs_live_not_offered
501
Batch jobs answer test keys only for now, because a job keeps the request and its result for a while: use a test key, with invented data.
invalid_job_route
400
route names the one route the job runs, as "POST /v1/claims/validate". A job runs: POST /v1/claims/validate, POST /v1/claims/837p, POST /v1/remittances/explain, POST /v1/eligibility/explain, POST /v1/claim-status/explain, POST /v1/analytics/summary, POST /v1/remittances/variance, POST /v1/claims/track, POST /v1/eligibility/270.
invalid_job
400
A job is { route, query? }: route the one route it runs, query the query string every call carries (no leading ?), or left out.
invalid_job_id
400
A job id is job_ and 24 letters or digits, as POST /v1/jobs answered it.
job_not_found
404
No job of this key has that id. A finished job is kept 24 hours.
job_not_cancellable
409
This job has already finished, so there is nothing to cancel: DELETE it to forget it now.
job_running
409
This job is running: cancel it first (POST /v1/jobs/{id}/cancel), then delete it once it says canceled.
jobs_daily_limit
429
This account's keys have made the most jobs an account may make today (50 a UTC day, its keys together). Try again tomorrow.
no_information_request
400
Send the payer's 277 request for additional information as x12 (the file's text).
not_an_information_request
422
This file is not a 277 request for additional information (005010X213): the error names what it is, and the route that reads it.
no_requested_claims
422
This request holds no claim (no TRN in a patient loop), so there is nothing to say or check.
invalid_information_request_call
400
A field of the request is not in the shape this route takes; each problem is listed with its field.
no_attachment_response
400
Send the solicited 275 to check as x12_275: the file's text, or a list of them.
not_an_attachment_response
422
A file sent as x12_275 is not a 275 this route can read: the error names the file and why.
invalid_audit_type
400
type is one of the audit log's entry types, or the part before its dot (key, webhook, enrollment...), as the developers page lists them.
invalid_audit_cursor
400
starting_after is the next_cursor of an earlier page of this audit log.
org_not_offered
501
Organizations and member sign-in are not open on this deployment yet. Keys still work as before.
org_session_required
401
This route needs a member's console sign-in, not an API key. Sign in at payerworks.com/console.
org_session_invalid
401
The sign-in could not be checked, or it has ended. Sign in again.
org_session_not_accepted
403
A member's sign-in manages an account; it does not make this call. Call this route with an API key.
org_email_not_verified
403
Confirm this sign-in's email address first: the code was emailed when the sign-in was made.
org_choose
400
This sign-in belongs to several organizations: name one in the Payerworks-Organization header (GET /v1/org/memberships lists them).
org_not_found
404
No organization with that id has this sign-in as a member.
org_not_a_member
403
This sign-in belongs to no organization yet. Make one with POST /v1/org, or open an invitation's link.
org_role_forbidden
403
Your role in this organization does not allow that. GET /v1/org/roles says which role does.
org_owner_only
403
Only an owner may do that: invite or make an owner, change or remove an owner, revoke a removed member's keys, or change the organization's settings.
org_last_owner
409
An organization always keeps at least one owner. Make another member an owner first.
org_member_not_found
404
This organization has no member with that id.
invalid_org_member_id
400
A member id is pwm_ followed by 16 lowercase hexadecimal characters.
org_invitation_not_found
404
No pending invitation matches. It may have been accepted or canceled; ask for a new one.
invalid_org_invitation_id
400
An invitation id is pwi_ followed by 16 lowercase hexadecimal characters.
org_invitation_expired
410
An invitation's link works for 7 days. Ask a member of the organization for a new one.
org_invitation_email_mismatch
403
This invitation was sent to another address. Sign in with the address the email reached.
org_already_member
409
That person is already a member of this organization.
org_invitation_pending
409
An invitation to that address is already waiting. Cancel it to send another.
org_account_taken
409
That key's account already belongs to an organization. Ask one of its owners to invite you.
org_key_not_usable
403
The api_key given is not a usable key (unknown, revoked or expired), so it proves no account.
org_limit_reached
409
An organization holds at most 200 members and 50 pending invitations, and a sign-in belongs to at most 20 organizations.
org_invitations_daily_limit
429
An organization sends at most 25 invitations a UTC day. Send more tomorrow.
invalid_org_request
400
The request is not what this route takes: errors names each field and why.
org_acting_key_required
400
Name the key this change is for in the Payerworks-Key header (its key id; GET /v1/org/keys lists them).
org_acting_key_not_found
404
The organization's account has no usable key with that id.
keys_managed_by_members
403
This account's organization chose that only its signed-in members manage keys. Make, rotate or revoke the key at payerworks.com/console.
org_changed_meanwhile
409
The organization changed while this request was made. Read it again and send the change again.
submit_not_offered
501
Payerworks does not send this claim yet: `gates` names each gate and whether it is open. Nothing was written or sent; write the 837P with POST /v1/claims/837p and send it on the connection you have.
submit_envelope_assigned
400
On a submission Payerworks sets the interchange: leave out sender_id, receiver_id, the two qualifiers, control_number and usage.
invalid_partner_id
400
partner_id is a partner's id as GET /v1/partners lists it, or left out to send to the one partner taking the claims' payer.
reference_prefix_required
422
Every claim's patientControlNumber must begin with your account's reference prefix, so each answer can be matched to you alone; the error names the prefix and the claims.
payer_not_at_partner
422
A claim names a payer this partner does not take claims for; send it in a call of its own to the partner that does.
reference_in_flight
409
A claim with this patientControlNumber was already sent to this partner and has not been rejected; a corrected claim (frequency 7 or 8) may reuse it.
submit_not_sent
503
The file was written but the partner's connection did not take it just now, so nothing was sent (`transmitted: false`). Send the call again.
not_found
404
No such route. The routes are listed at payerworks.com/developers.
method_not_allowed
405
This route does not answer that method; the allow header names the ones it does.
no_key
401
Send your API key as `Authorization: Bearer <key>`.
invalid_key
401
That key is not valid. Keys are issued by Payerworks; a revoked key stops within a minute.
rate_limited
429
Over this key's calls a minute. Wait the seconds retry-after names.
too_large
413
A request body may be at most 2 MB.
invalid_json
400
The body must be a JSON object.
invalid_idempotency_key
400
An Idempotency-Key is 1 to 255 visible characters, with no spaces, quotes or backslashes. A UUID is a good one.
idempotency_key_reused
422
This Idempotency-Key was used in the last 24 hours for a different request. Send a new key for a new request.
idempotency_in_flight
409
The first request with this Idempotency-Key is still running. Retry after the seconds retry-after names.
idempotency_superseded
409
What this request set has since been changed or removed, so its answer cannot be given again. Send a new request with a new Idempotency-Key.
internal
500
Something failed on our side. Retry; if it keeps failing, write to us with the request_id.
store_unavailable
503
We could not read or write this key's records just now; the message says what was not done. Retry in a moment.
queue_unavailable
503
We could not queue the events for delivery just now. Retry in a moment.
invalid_webhook_url
400
A webhook address is https, at a public hostname, on port 443, with no login, at most 500 characters.
invalid_rotate
400
rotate is true or false, or left out.
webhook_conflict
409
The webhook changed while this request ran, so nothing was saved. Send the request again.
no_webhook
409
This key has no webhook yet: set one with PUT /v1/account/webhook first.
too_many_events
413
This run made more events than one delivery carries, so none were queued. Send fewer claims a call.
webhook_rate_limited
429
Over this key's webhook deliveries a minute, so nothing was queued. Wait for the next minute.
event_limit_reached
429
This account's keys have recorded the 2,000 events an account keeps in a UTC day, so no more are recorded or sent this UTC day. Events are recorded again from 00:00 UTC; an event refused before then is never sent by itself.

Talk to us

What happens next

  1. 1

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

  2. 2

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

  3. 3

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

Or write to hello@payerworks.com.

Talk to Payerworks

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

Used to reply to you and for nothing else.