Skip to content

Docs: by job

Batch jobs

For more than one call can carry: many calls to one route from one file you upload, answered off the API’s clock, the result kept 24 hours.

Read as markdown

Payerworks writes and explains files; it sends nothing to a payer. Every file a route writes says transmitted: false, and is yours to send on your own connection. A test key is for invented data only, never a real patient.

For more than one call can carry. Make a job naming the one route it runs, upload one file of calls to the signed form it answers, and read the job until it finishes: each call is answered as that route answers it, one after another, off the API’s ten-second clock, and the result is a file you fetch by a signed link. A job runs claim validation, 837P writing, the remittance, eligibility and claim status explanations, analytics, payment variance and claim tracking; POST /v1/eligibility/270 as a job writes one 270 of up to 7,500 questions.

A job is the one part of the API that keeps what you send for a while: your file until the job finishes, then it is deleted, and the result for 24 hours after, in storage of its own that keeps no earlier copy and is in no backup. Batch jobs take test keys only, for invented data, and are off until Payerworks turns them on: until then every jobs route answers 501 jobs_not_offered.

Routes

Each route shows its call with curl and with the two clients, then its real answer, shortened, and the fields it holds. The client calls assume one client made once, const pw = new Payerworks() in JavaScript or pw = payerworks.Payerworks() in Python, each reading PAYERWORKS_API_KEY and PAYERWORKS_BASE_URL, and body is the request.json shown.

POST/v1/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.

Request

curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/jobs" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d @request.json
request.jsonJSON
{
  "route": "POST /v1/claims/validate"
}
The answer (201)JSON
{
  "id": "job_9e6d21650c98b40f5b5848da",
  "object": "job",
  "route": "POST /v1/claims/validate",
  "query": "",
  "mode": "test",
  "status": "awaiting_upload",
  "status_words": "Waiting for the request file: upload it with the signed form in `upload`. Nothing runs until it lands.",
  "created_at": "2026-10-20T19:12:00.142Z",
  "started_at": null,
  "finished_at": null,
  "upload": {
    "method": "POST",
    "url": "https://pw-jobs-example.s3.us-east-1.amazonaws.com/",
    "fields": {
      "key": "requests/pwk_0123456789abcdef01/job_9e6d21650c98b40f5b5848da.json",
      "Content-Type": "application/json",
      "x-amz-server-side-encryption": "aws:kms",
      "x-amz-server-side-encryption-aws-kms-key-id": "arn:aws:kms:us-east-1:111111111111:key/example",
      "x-amz-algorithm": "AWS4-HMAC-SHA256",
      "x-amz-credential": "AKIAEXAMPLE/20261020/us-east-1/s3/aws4_request",
      "x-amz-date": "20261020T191200Z",
      "policy": "eyJleHBpcmF0aW9uIjoiMjAyNi0xMC0yMFQyMDoxMjowMC4xNDJaIiwiY29uZGl0aW9ucyI6W3siYnVja2V0IjoicHctam9icy1leGFtcGxlIn0seyJrZXkiOiJyZXF1ZXN0cy9wd2tfMDEyMzQ1Njc4OWFiY2RlZjAxL2pvYl85ZTZkMjE2NTBjOThiNDBmNWI1ODQ4ZGEuanNvbiJ9LHsiQ29udGVudC1UeXBlIjoiYXBw…",
      "x-amz-signature": "492a2aacfb34613b509d8d879e7e11a35e16289190fffc040a722cebd08ffc59"
    },
    "expires_at": "2026-10-20T20:12:00.142Z",
    "max_bytes": 50000000,
    "content_type": "application/json",
    "file_field": "file"
  },
  "calls": null,
  "result": null,
  "result_kept_until": null,
  "error": null,
  "kept": "A job keeps your request file until the job finishes, then deletes it, and keeps the result for 24 hours after the job finishes, fetched by a signed link; after that the job is gone and the files are deleted from storage in the day or so af…",
  "stored": true,
  "transmitted": false
}

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

Field, Type, What it is
FieldTypeWhat it is
idstringjob_ and 24 lowercase hexadecimal characters.
objectthe value "job"
routestring
querystring
modeone of "test", "live"
statusone of "awaiting_upload", "running", "completed", "partial", "failed", "canceled", "expired"
status_wordsstring
created_atstring
started_atstring or null
finished_atstring or null
uploadJobUpload or null
callsobject or null
resultobject or null
result_kept_untilstring or null
errorobject or null
cancel_requestedbooleanPresent while a cancel waits for the run to stop.
keptstringA job keeps your request file until the job finishes, then deletes it, and keeps the result for 24 hours after the job finishes, fetched by a signed link; after that the job is gone and the files are deleted from storage in the day or so after. Every other route answers and keeps nothing.
storedthe value true
transmittedthe value false

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.

Request

curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/jobs" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY"
The answer (200)JSON
{
  "data": [
    {
      "id": "job_9e6d21650c98b40f5b5848da",
      "object": "job",
      "route": "POST /v1/claims/validate",
      "query": "",
      "mode": "test",
      "status": "awaiting_upload",
      "status_words": "Waiting for the request file: upload it with the signed form in `upload`. Nothing runs until it lands.",
      "created_at": "2026-10-20T19:12:00.142Z",
      "started_at": null,
      "finished_at": null,
      "upload": null,
      "calls": null,
      "result": null,
      "result_kept_until": null,
      "error": null,
      "kept": "A job keeps your request file until the job finishes, then deletes it, and keeps the result for 24 hours after the job finishes, fetched by a signed link; after that the job is gone and the files are deleted from storage in the day or so af…",
      "stored": true,
      "transmitted": false
    }
  ],
  "kept": "A job keeps your request file until the job finishes, then deletes it, and keeps the result for 24 hours after the job finishes, fetched by a signed link; after that the job is gone and the files are deleted from storage in the day or so af…"
}

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

Field, Type, What it is
FieldTypeWhat it is
datalist of JobAt most 100, newest first.
keptstringA job keeps your request file until the job finishes, then deletes it, and keeps the result for 24 hours after the job finishes, fetched by a signed link; after that the job is gone and the files are deleted from storage in the day or so after. Every other route answers and keeps nothing.

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.

Request

curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/jobs/job_9e6d21650c98b40f5b5848da" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY"
The answer (200)JSON
{
  "id": "job_9e6d21650c98b40f5b5848da",
  "object": "job",
  "route": "POST /v1/claims/validate",
  "query": "",
  "mode": "test",
  "status": "awaiting_upload",
  "status_words": "Waiting for the request file: upload it with the signed form in `upload`. Nothing runs until it lands.",
  "created_at": "2026-10-20T19:12:00.142Z",
  "started_at": null,
  "finished_at": null,
  "upload": {
    "method": "POST",
    "url": "https://pw-jobs-example.s3.us-east-1.amazonaws.com/",
    "fields": {
      "key": "requests/pwk_0123456789abcdef01/job_9e6d21650c98b40f5b5848da.json",
      "Content-Type": "application/json",
      "x-amz-server-side-encryption": "aws:kms",
      "x-amz-server-side-encryption-aws-kms-key-id": "arn:aws:kms:us-east-1:111111111111:key/example",
      "x-amz-algorithm": "AWS4-HMAC-SHA256",
      "x-amz-credential": "AKIAEXAMPLE/20261020/us-east-1/s3/aws4_request",
      "x-amz-date": "20261020T191200Z",
      "policy": "eyJleHBpcmF0aW9uIjoiMjAyNi0xMC0yMFQyMDoxMTo1OS4xNDdaIiwiY29uZGl0aW9ucyI6W3siYnVja2V0IjoicHctam9icy1leGFtcGxlIn0seyJrZXkiOiJyZXF1ZXN0cy9wd2tfMDEyMzQ1Njc4OWFiY2RlZjAxL2pvYl85ZTZkMjE2NTBjOThiNDBmNWI1ODQ4ZGEuanNvbiJ9LHsiQ29udGVudC1UeXBlIjoiYXBw…",
      "x-amz-signature": "2270f9df8728a7b0c33005d9d2100aa75795c1fb302c710b879b83442299f4a6"
    },
    "expires_at": "2026-10-20T20:11:59.147Z",
    "max_bytes": 50000000,
    "content_type": "application/json",
    "file_field": "file"
  },
  "calls": null,
  "result": null,
  "result_kept_until": null,
  "error": null,
  "kept": "A job keeps your request file until the job finishes, then deletes it, and keeps the result for 24 hours after the job finishes, fetched by a signed link; after that the job is gone and the files are deleted from storage in the day or so af…",
  "stored": true,
  "transmitted": false
}

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

Field, Type, What it is
FieldTypeWhat it is
idstringjob_ and 24 lowercase hexadecimal characters.
objectthe value "job"
routestring
querystring
modeone of "test", "live"
statusone of "awaiting_upload", "running", "completed", "partial", "failed", "canceled", "expired"
status_wordsstring
created_atstring
started_atstring or null
finished_atstring or null
uploadJobUpload or null
callsobject or null
resultobject or null
result_kept_untilstring or null
errorobject or null
cancel_requestedbooleanPresent while a cancel waits for the run to stop.
keptstringA job keeps your request file until the job finishes, then deletes it, and keeps the result for 24 hours after the job finishes, fetched by a signed link; after that the job is gone and the files are deleted from storage in the day or so after. Every other route answers and keeps nothing.
storedthe value true
transmittedthe value false

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.

Request

curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/jobs/job_9e6d21650c98b40f5b5848da/cancel" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY"
The answer (200)JSON
{
  "id": "job_9e6d21650c98b40f5b5848da",
  "object": "job",
  "route": "POST /v1/claims/validate",
  "query": "",
  "mode": "test",
  "status": "canceled",
  "status_words": "Canceled: nothing more runs and no result is kept.",
  "created_at": "2026-10-20T19:12:00.142Z",
  "started_at": null,
  "finished_at": "2026-10-20T19:12:00.150Z",
  "upload": null,
  "calls": null,
  "result": null,
  "result_kept_until": null,
  "error": null,
  "kept": "A job keeps your request file until the job finishes, then deletes it, and keeps the result for 24 hours after the job finishes, fetched by a signed link; after that the job is gone and the files are deleted from storage in the day or so af…",
  "stored": true,
  "transmitted": false
}

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

Field, Type, What it is
FieldTypeWhat it is
idstringjob_ and 24 lowercase hexadecimal characters.
objectthe value "job"
routestring
querystring
modeone of "test", "live"
statusone of "awaiting_upload", "running", "completed", "partial", "failed", "canceled", "expired"
status_wordsstring
created_atstring
started_atstring or null
finished_atstring or null
uploadJobUpload or null
callsobject or null
resultobject or null
result_kept_untilstring or null
errorobject or null
cancel_requestedbooleanPresent while a cancel waits for the run to stop.
keptstringA job keeps your request file until the job finishes, then deletes it, and keeps the result for 24 hours after the job finishes, fetched by a signed link; after that the job is gone and the files are deleted from storage in the day or so after. Every other route answers and keeps nothing.
storedthe value true
transmittedthe value false

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.

Request

curl -sS -X DELETE "$PAYERWORKS_BASE_URL/v1/jobs/job_9e6d21650c98b40f5b5848da" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY"
The answer (200)JSON
{
  "id": "job_9e6d21650c98b40f5b5848da",
  "deleted": true,
  "kept": "A job keeps your request file until the job finishes, then deletes it, and keeps the result for 24 hours after the job finishes, fetched by a signed link; after that the job is gone and the files are deleted from storage in the day or so af…"
}

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

Field, Type, What it is
FieldTypeWhat it is
idstring
deletedthe value true
keptstringA job keeps your request file until the job finishes, then deletes it, and keeps the result for 24 hours after the job finishes, fetched by a signed link; after that the job is gone and the files are deleted from storage in the day or so after. Every other route answers and keeps nothing.

Reading the answer

  • A job’s status is one of awaiting_upload, running, completed, partial, failed, canceled or expired, with its status_words. upload is the signed form while the upload window is open; post every field as given, then the file under file_field. Once it closes, a job still awaiting_upload says so in its status_words, and is called expired only 65 minutes after the window closed, because a file that landed in time may still be waiting to start.
  • Once it has finished, calls counts the answers by status and result.url is a link good for 15 minutes; ask again for a new one. The result is { answers: [ { index, status, body } ] }, each body 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 call to send again. A failed job says why in error.code.
  • A job.completed or job.failed event reaches your webhook and destinations when the job ends. It carries the job’s id, route, state and counts, never the result or its link.

Troubleshooting

  • jobs_not_offered (501): Batch jobs are off on this host until Payerworks turns them on. A 501 is never retried by the clients: it will not pass by waiting.
  • jobs_live_not_offered (501): A job keeps your file and its result for a while, so it takes test keys only for now: use a test key, with invented data.
  • invalid_job_route (400): A job runs one of the routes listed in the message. A store route, a sandbox, a PDF or a model read is never a job.
  • job_running (409): A running job cannot be deleted: cancel it, then delete it once it says canceled.
  • jobs_daily_limit (429): An account’s keys together may make 50 jobs a UTC day.
  • One call is never made larger by a job: a call over 2 MB is answered 413 too_large at its index in the result. Split the file, or send many files as many calls.

Every refusal these routes may answer, with its status. Any route may also answer the codes every route shares, on Every error.

Code, Status, What it means
CodeStatusWhat it means
invalid_job400A job is { route, query? }: route the one route it runs, query the query string every call carries (no leading ?), or left out.
invalid_job_id400A job id is job_ and 24 letters or digits, as POST /v1/jobs answered it.
invalid_job_route400route 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_query400A query parameter's name or value is over 200 characters, or holds a percent escape that does not decode.
job_not_found404No job of this key has that id. A finished job is kept 24 hours.
job_not_cancellable409This job has already finished, so there is nothing to cancel: DELETE it to forget it now.
job_running409This job is running: cancel it first (POST /v1/jobs/{id}/cancel), then delete it once it says canceled.
jobs_daily_limit429This account's keys have made the most jobs an account may make today (50 a UTC day, its keys together). Try again tomorrow.
jobs_live_not_offered501Batch 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.
jobs_not_offered501Batch 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.
store_unavailable503We could not read or write this key's records just now; the message says what was not done. Retry in a moment.

Code lists

Why a job failed (error.code)

Code, What it means
CodeWhat it means
job_file_not_jsonThe uploaded file is not JSON.
job_file_shapeThe uploaded file is not this route's job: send { "calls": [ <body>, ... ] }, each body what the route takes in one call (for POST /v1/eligibility/270, the route's own body with its requests).
job_too_many_callsThe uploaded file holds more calls than one job runs: at most 1,000. Split it into more jobs.
job_too_many_requestsThe batch 270 holds more requests than one file is written from: at most 7,500.
job_file_too_largeThe uploaded file is larger than a job reads: at most 50,000,000 bytes.
job_key_not_usableThe key that made this job was revoked, expired or replaced before the job ran, so nothing was run.
job_stoppedThe run stopped before it finished (on our side): nothing was kept. Make the job again.
job_internalSomething failed on our side while the job ran: nothing was kept. Make the job again; if it keeps failing, write to us with the job id.
job_result_too_largeNot even the first answer fits a job's result (250,000,000 bytes): send less in each call.
job_270_too_many_segmentsThe batch 270 written from these requests holds more segments than one 270 is read in (limit_segments, our own reader's bound): nothing was kept. Send fewer requests in each job, or fewer procedures in each request.
job_not_startedYour file arrived, but the job could not be started on our side within 60 minutes of its arrival: nothing was run and the file was deleted. Make the job again.

Example

jobs.mjs runs every step below against the API with a test key. It needs payerworks.mjs and sample-data.mjs beside it.

jobs.mjsJavaScript
// Payerworks example: a batch job, many calls to one route from one file. Docs: https://payerworks.com/docs/jobs
//
// Run it with Node 18 or later, with the JavaScript client (https://payerworks.com/sdk/payerworks.mjs)
// and sample-data.mjs beside this file:
//   PAYERWORKS_API_KEY=pw_test_... PAYERWORKS_BASE_URL=<the base URL shown with your key> node jobs.mjs
// A test key and invented data only. A job keeps its file until it finishes and its result for 24 hours.
// Payerworks sends nothing to a payer: every file it writes is yours.
import { pathToFileURL } from 'node:url';
import { Payerworks, PayerworksError } from './payerworks.mjs';
import { CLAIM, copy, refusalOf } from './sample-data.mjs';

/** @param {Payerworks} pw */
export async function run(pw) {
  // Batch jobs are off until Payerworks turns them on. Where they are not, every jobs route answers
  // 501 jobs_not_offered (never retried: it will not pass by waiting), and each call is the same
  // work sent to its own route.
  let job = null;
  const off = await refusalOf(async () => { job = await pw.postJobs({ route: 'POST /v1/claims/validate' }); }, { PayerworksError });
  if (off?.code === 'jobs_not_offered') return { offered: false, refused: off };
  if (off) return { offered: null, refused: off };
  // Where they are on: one file of calls, each exactly the body POST /v1/claims/validate takes.
  const read = await pw.uploadJobFile(job, { calls: [{ claim: copy(CLAIM) }, { claim: copy(CLAIM) }] });
  // The failure path: a route a job never runs.
  const refused = await refusalOf(() => pw.postJobs({ route: 'POST /v1/remittances/from-image' }), { PayerworksError });
  return { offered: true, status: read.status, kept: read.kept, refused };
}

// Run as a script: node <this file>, with PAYERWORKS_API_KEY and PAYERWORKS_BASE_URL set.
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
  console.log(JSON.stringify(await run(new Payerworks()), null, 2));
}
What it answered, run against the API in Payerworks’ own tests (your request ids will differ)JSON
{
  "offered": false,
  "refused": {
    "status": 501,
    "code": "jobs_not_offered",
    "request_id": "req_1"
  }
}

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.