Skip to content

Docs: by job

Events and webhooks

Every answer the claims sandbox makes, and every change to an enrollment request, posted to you as a signed event and kept for 30 days.

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.

An event is a row before it is sent: every answer the claims sandbox makes, and every change to an enrollment request, is recorded on your account and posted to your webhook, and to each webhook destination you add, signed with a secret only you and Payerworks hold. Every event is kept for 30 days, to list and to send again.

Your server may receive an event more than once. Each event carries one id in the webhook-id header: keep the ids you have handled and skip one you have seen.

New here? Start with Following an event.

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.

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

Request

curl -sS -X PUT "$PAYERWORKS_BASE_URL/v1/account/webhook" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d @request.json
request.jsonJSON
{
  "url": "https://hooks.example.com/payerworks/events"
}
The answer (200)JSON
{
  "url": "https://hooks.example.com/payerworks/events",
  "secret": "whsec_bTqF5EiMSt1kZ4Sh577Ya/UQMCWse+eDI5CyF9JdOsk=",
  "secret_last_four": "Osk=",
  "created_at": "2026-10-19T15:00:00.000Z",
  "rotated": false,
  "previous_secret_expires_at": null
}
Field, Type, What it is
FieldTypeWhat it is
urlstring
secretstring or nullwhsec_ and the secret. Set only when this request made one (the first set, or rotate: true); null otherwise.
secret_last_fourstring
created_atstring
rotatedboolean
previous_secret_expires_atstring or null

DELETE/v1/account/webhook

Remove your webhook

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

Request

curl -sS -X DELETE "$PAYERWORKS_BASE_URL/v1/account/webhook" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY"
The answer (200)JSON
{
  "deleted": true
}
Field, Type, What it is
FieldTypeWhat it is
deletedthe value true

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.

Request

curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/account/webhook/test" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY"
The answer (202)JSON
{
  "queued": true,
  "event_id": "evt_7cb356efb22e856293c87091"
}
Field, Type, What it is
FieldTypeWhat it is
queuedthe value true
event_idstringThe event’s id: GET /v1/events/{id} says what your server did with it.
destination_idstringA redelivery to a webhook destination: which one.

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.

Request

curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/events" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY"
The answer (200)JSON
{
  "data": [
    {
      "id": "evt_4fbc127e84a0331dfb743e10",
      "type": "sandbox.remittance.received",
      "created": 1792422000,
      "mode": "test",
      "data": {
        "check_number": "PWSANDBOX-000000003-1",
        "payment_date": "20261019",
        "total": 1392,
        "claims": [
          {
            "reference": "PW-SBX-PAID",
            "payer_claim_number": "PWS2292711347",
            "status_code": "1",
            "status": "processed as primary",
            "paid": 1392
          }
        ]
      },
      "delivery": {
        "state": "delivered",
        "attempts": 1,
        "last_status": 200,
        "last_attempt_at": "2026-10-19T15:00:00.000Z",
        "last_error": null,
        "delivered_at": "2026-10-19T15:00:00.000Z"
      }
    },
    {
      "id": "evt_e38518c4523f2dcd24e34de2",
      "type": "sandbox.claim.acknowledged",
      "created": 1792422000,
      "mode": "test",
      "data": {
        "reference": "PW-SBX-PAID",
        "payer_claim_number": "PWS2292711347",
        "verdict": "A",
        "fixes": [
          "The claim was accepted into the payer's adjudication system: accepted for processing (STC A2:20). Nothing to fix."
        ]
      },
      "delivery": {
        "state": "delivered",
        "attempts": 1,
        "last_status": 200,
        "last_attempt_at": "2026-10-19T15:00:00.000Z",
        "last_error": null,
        "delivered_at": "2026-10-19T15:00:00.000Z"
      }
    }
  ],
  "has_more": true,
  "next_cursor": "cur_45233137393234313634363138393523303031236576745f376362333536656662323265383536323933633837303931"
}
Field, Type, What it is
FieldTypeWhat it is
datalist of EventNewest first.
has_moreboolean
next_cursorstring or nullPass as starting_after for the next page.

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.

Request

curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/events/evt_4ba3d86a9cb07e1b79f6e144" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY"
The answer (200)JSON
{
  "id": "evt_4fbc127e84a0331dfb743e10",
  "type": "sandbox.remittance.received",
  "created": 1792422000,
  "mode": "test",
  "data": {
    "check_number": "PWSANDBOX-000000003-1",
    "payment_date": "20261019",
    "total": 1392,
    "claims": [
      {
        "reference": "PW-SBX-PAID",
        "payer_claim_number": "PWS2292711347",
        "status_code": "1",
        "status": "processed as primary",
        "paid": 1392
      }
    ]
  },
  "delivery": {
    "state": "delivered",
    "attempts": 1,
    "last_status": 200,
    "last_attempt_at": "2026-10-19T15:00:00.000Z",
    "last_error": null,
    "delivered_at": "2026-10-19T15:00:00.000Z"
  }
}
Field, Type, What it is
FieldTypeWhat it is
idstringevt_ and hex. The same each time the same event is sent.
typeone of "ping", "sandbox.file.acknowledged", "sandbox.claim.acknowledged", "sandbox.remittance.received", "enrollment.updated", "enrollment.live", "enrollment.rejected", "enrollment.canceled", "enrollment.ended", "enrollment.task.assigned", "enrollment.task.completed", "enrollment.task.deleted", "job.completed", "job.failed", "submission.file.acknowledged", "submission.claim.acknowledged", "submission.remittance.received"
createdintegerUnix seconds.
modeone of "test", "live"The key’s: an enrollment event of a live key is live, and a submission event always is; a ping and the sandbox payer’s events are always test.
dataobjectThe event’s own facts: for a sandbox event, invented answers, your claim references and your file’s control numbers; for an enrollment event, ids, statuses and words only; for a submission event, your references, the control numbers and the partner’s verdicts and amounts, never a name or a member ID.
deliveryEventDelivery

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.

Request

curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/events/evt_4ba3d86a9cb07e1b79f6e144/redeliver" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d @request.json
request.jsonJSON
{
  "destination_id": "wd_4e1687f5d8638cbd"
}
The answer (202)JSON
{
  "queued": true,
  "event_id": "evt_4fbc127e84a0331dfb743e10",
  "destination_id": "wd_4e1687f5d8638cbd"
}
Field, Type, What it is
FieldTypeWhat it is
queuedthe value true
event_idstringThe event’s id: GET /v1/events/{id} says what your server did with it.
destination_idstringA redelivery to a webhook destination: which one.

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.

Request

curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/events/evt_298da382d80136d9249cf920/attempts" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY"
The answer (200)JSON
{
  "event_id": "evt_298da382d80136d9249cf920",
  "data": [
    {
      "event_id": "evt_298da382d80136d9249cf920",
      "destination_id": "wd_4e1687f5d8638cbd",
      "number": 1,
      "type": "automatic",
      "delivered": false,
      "status": 503,
      "error": "The endpoint answered 503; only a 2xx counts as delivered.",
      "at": "2026-10-20T19:12:00.093Z"
    }
  ]
}
Field, Type, What it is
FieldTypeWhat it is
event_idstring
datalist of objectEach post: its headers carry Payerworks-Attempt-Number, Payerworks-Attempt-Type, Payerworks-Destination-Id.

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.

Request

curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/webhook-destinations" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY"
The answer (200)JSON
{
  "data": [
    {
      "id": "wd_4e1687f5d8638cbd",
      "name": "Primary",
      "url": "https://hooks.example.com/payerworks/primary",
      "event_types": [
        "sandbox.claim.acknowledged",
        "sandbox.remittance.received"
      ],
      "enabled": true,
      "concurrency": 2,
      "secret_last_four": "J+c=",
      "previous_secret_expires_at": null,
      "disabled_at": null,
      "disabled_reason": null,
      "failing_since": null,
      "notices": [],
      "created_at": "2026-10-20T19:12:00.036Z",
      "updated_at": "2026-10-20T19:12:00.036Z"
    }
  ]
}
Field, Type, What it is
FieldTypeWhat it is
datalist of WebhookDestinationAt most 10.

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.

Request

curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/webhook-destinations" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d @request.json
request.jsonJSON
{
  "name": "Primary",
  "url": "https://hooks.example.com/payerworks/primary",
  "event_types": [
    "sandbox.claim.acknowledged",
    "sandbox.remittance.received"
  ],
  "concurrency": 2
}
The answer (201)JSON
{
  "id": "wd_4e1687f5d8638cbd",
  "name": "Primary",
  "url": "https://hooks.example.com/payerworks/primary",
  "event_types": [
    "sandbox.claim.acknowledged",
    "sandbox.remittance.received"
  ],
  "enabled": true,
  "concurrency": 2,
  "secret_last_four": "J+c=",
  "previous_secret_expires_at": null,
  "disabled_at": null,
  "disabled_reason": null,
  "failing_since": null,
  "notices": [],
  "created_at": "2026-10-20T19:12:00.036Z",
  "updated_at": "2026-10-20T19:12:00.036Z",
  "secret": "whsec_Y9WgA88flTmkMq89BiZzDzjILhNojHvXhMpB0Td1J+c="
}
Field, Type, What it is
FieldTypeWhat it is
idstringwd_ and 16 hexadecimal characters.
namestring
urlstring
event_typeslist of one of "ping", "sandbox.file.acknowledged", "sandbox.claim.acknowledged", "sandbox.remittance.received", "enrollment.updated", "enrollment.live", "enrollment.rejected", "enrollment.canceled", "enrollment.ended", "enrollment.task.assigned", "enrollment.task.completed", "enrollment.task.deleted", "job.completed", "job.failed", "submission.file.acknowledged", "submission.claim.acknowledged", "submission.remittance.received"Empty: every type.
enabledboolean
concurrencyinteger
secret_last_fourstring
previous_secret_expires_atstring or null
disabled_atstring or null
disabled_reasonstring or nullretries_exhausted, when Payerworks disabled it.
failing_sincestring or null
noticeslist of object
created_atstring
updated_atstring
secretstringwhsec_ and the secret: only in the answer that made it (the create, or rotate: true).

GET/v1/webhook-destinations/{id}

One webhook destination

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

Request

curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/webhook-destinations/wd_4e1687f5d8638cbd" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY"
The answer (200)JSON
{
  "id": "wd_4e1687f5d8638cbd",
  "name": "Primary",
  "url": "https://hooks.example.com/payerworks/primary",
  "event_types": [
    "sandbox.claim.acknowledged",
    "sandbox.remittance.received"
  ],
  "enabled": true,
  "concurrency": 2,
  "secret_last_four": "J+c=",
  "previous_secret_expires_at": null,
  "disabled_at": null,
  "disabled_reason": null,
  "failing_since": null,
  "notices": [],
  "created_at": "2026-10-20T19:12:00.036Z",
  "updated_at": "2026-10-20T19:12:00.036Z"
}
Field, Type, What it is
FieldTypeWhat it is
idstringwd_ and 16 hexadecimal characters.
namestring
urlstring
event_typeslist of one of "ping", "sandbox.file.acknowledged", "sandbox.claim.acknowledged", "sandbox.remittance.received", "enrollment.updated", "enrollment.live", "enrollment.rejected", "enrollment.canceled", "enrollment.ended", "enrollment.task.assigned", "enrollment.task.completed", "enrollment.task.deleted", "job.completed", "job.failed", "submission.file.acknowledged", "submission.claim.acknowledged", "submission.remittance.received"Empty: every type.
enabledboolean
concurrencyinteger
secret_last_fourstring
previous_secret_expires_atstring or null
disabled_atstring or null
disabled_reasonstring or nullretries_exhausted, when Payerworks disabled it.
failing_sincestring or null
noticeslist of object
created_atstring
updated_atstring
secretstringwhsec_ and the secret: only in the answer that made it (the create, or rotate: true).

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.

Request

curl -sS -X PUT "$PAYERWORKS_BASE_URL/v1/webhook-destinations/wd_4e1687f5d8638cbd" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d @request.json
request.jsonJSON
{
  "rotate": true,
  "overlap_hours": 48
}
The answer (200)JSON
{
  "id": "wd_4e1687f5d8638cbd",
  "name": "Primary",
  "url": "https://hooks.example.com/payerworks/primary",
  "event_types": [
    "sandbox.claim.acknowledged",
    "sandbox.remittance.received"
  ],
  "enabled": true,
  "concurrency": 2,
  "secret_last_four": "nHY=",
  "previous_secret_expires_at": "2026-10-22T19:12:00.046Z",
  "disabled_at": null,
  "disabled_reason": null,
  "failing_since": null,
  "notices": [],
  "created_at": "2026-10-20T19:12:00.036Z",
  "updated_at": "2026-10-20T19:12:00.046Z",
  "secret": "whsec_+3n+g2Y/+EkwVwm/9GiroC62uAVWtcbDgxSrcoK2nHY="
}
Field, Type, What it is
FieldTypeWhat it is
idstringwd_ and 16 hexadecimal characters.
namestring
urlstring
event_typeslist of one of "ping", "sandbox.file.acknowledged", "sandbox.claim.acknowledged", "sandbox.remittance.received", "enrollment.updated", "enrollment.live", "enrollment.rejected", "enrollment.canceled", "enrollment.ended", "enrollment.task.assigned", "enrollment.task.completed", "enrollment.task.deleted", "job.completed", "job.failed", "submission.file.acknowledged", "submission.claim.acknowledged", "submission.remittance.received"Empty: every type.
enabledboolean
concurrencyinteger
secret_last_fourstring
previous_secret_expires_atstring or null
disabled_atstring or null
disabled_reasonstring or nullretries_exhausted, when Payerworks disabled it.
failing_sincestring or null
noticeslist of object
created_atstring
updated_atstring
secretstringwhsec_ and the secret: only in the answer that made it (the create, or rotate: true).

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.

Request

curl -sS -X DELETE "$PAYERWORKS_BASE_URL/v1/webhook-destinations/wd_4e1687f5d8638cbd" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY"
The answer (200)JSON
{
  "id": "wd_4e1687f5d8638cbd",
  "deleted": true
}
Field, Type, What it is
FieldTypeWhat it is
idstring
deletedthe value true

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.

Request

curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/webhook-destinations/wd_4e1687f5d8638cbd/ping" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY"
The answer (202)JSON
{
  "queued": true,
  "event_id": "evt_9b13ad1f89c0ff82ddbb2bf1",
  "destination_id": "wd_4e1687f5d8638cbd"
}
Field, Type, What it is
FieldTypeWhat it is
queuedthe value true
event_idstring
destination_idstring

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.

Request

curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/webhook-destinations/wd_4e1687f5d8638cbd/deliveries" \
  -H "Authorization: Bearer $PAYERWORKS_API_KEY"
The answer (200)JSON
{
  "data": [
    {
      "event_id": "evt_298da382d80136d9249cf920",
      "event_type": "sandbox.remittance.received",
      "destination_id": "wd_4e1687f5d8638cbd",
      "created_at": "2026-10-20T19:12:00.057Z",
      "state": "scheduled",
      "attempts": 1,
      "next_attempt_at": "2026-10-20T19:17:00.093Z",
      "last_attempt_at": "2026-10-20T19:12:00.093Z",
      "last_status": 503,
      "last_error": "The endpoint answered 503; only a 2xx counts as delivered.",
      "delivered_at": null
    },
    {
      "event_id": "evt_de844b60d20dade1dfdfdf52",
      "event_type": "sandbox.claim.acknowledged",
      "destination_id": "wd_4e1687f5d8638cbd",
      "created_at": "2026-10-20T19:12:00.057Z",
      "state": "scheduled",
      "attempts": 1,
      "next_attempt_at": "2026-10-20T19:17:00.093Z",
      "last_attempt_at": "2026-10-20T19:12:00.093Z",
      "last_status": 503,
      "last_error": "The endpoint answered 503; only a 2xx counts as delivered.",
      "delivered_at": null
    }
  ],
  "has_more": false,
  "next_cursor": null
}

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 DestinationDeliveryNewest first.
has_moreboolean
next_cursorstring or nullPass as starting_after.

Reading the answer

  • Each delivery carries the Standard Webhooks headers (webhook-id, webhook-timestamp, webhook-signature) and Payerworks-Signature. Check either with the clients’ verifiers, over the raw body, before you parse it.
  • GET /v1/events lists newest first, filtered by type, by delivery state and by when it was made; GET /v1/events/{id}/attempts lists every attempt, automatic or by hand.

Troubleshooting

  • invalid_webhook_url (400): A webhook address is https, at a public hostname, on port 443, with no login.
  • event_limit_reached (429): An account keeps at most its daily number of events; past it, nothing more is recorded until 00:00 UTC.
  • 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 signature that does not verify is almost always a body that was parsed and written again before the check. Check the raw bytes as they arrived.

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_concurrency400concurrency is a whole number from 1 to 10: how many deliveries this destination takes at once.
invalid_contact_email400email is an address of at most 254 characters, or null to remove it.
invalid_created400created_gte and created_lt are instants written like 2026-10-19T14:00:00Z, and created_gte comes before created_lt.
invalid_cursor400starting_after is the next_cursor of an earlier page, or the id of one of this key's events.
invalid_delivery_state400The delivery state filter names one of the states the developers page lists.
invalid_destination_id400A destination id is wd_ followed by 16 lowercase hexadecimal characters.
invalid_destination_name400A destination's name is 1 to 80 characters.
invalid_enabled400enabled is true or false, or left out.
invalid_event_id400An event id is evt_ followed by 16 to 64 lowercase hexadecimal characters.
invalid_event_type400type is one of the event types the developers page lists.
invalid_event_types400event_types is a list of the event types the developers page lists; an empty list, or leaving it out, receives every type.
invalid_expires_in_days400expires_in_days is a whole number from 1 to 365, or left out for a key that does not expire.
invalid_key_id400A key id is pwk_ followed by 16 to 40 letters and digits.
invalid_key_name400A key's name is 1 to 80 characters.
invalid_limit400limit is a whole number from 1 to 100.
invalid_overlap_hours400overlap_hours is a whole number from 0 to 720: how long the old secret is still accepted after a rotation.
invalid_rotate400rotate is true or false, or left out.
invalid_webhook_url400A webhook address is https, at a public hostname, on port 443, with no login, at most 500 characters.
contact_not_proved403Notices 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.
destination_not_found404This key has no webhook destination with that id.
event_not_found404No event of this key has that id. Events are kept for 30 days.
key_not_found404This account has no key with that id.
not_at_destination404That 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.
destination_conflict409The destination changed while this request ran, so nothing was saved. Send the request again.
destination_disabled409This destination is disabled. Enable it with PUT /v1/webhook-destinations/{id} and enabled: true first.
destination_limit_reached409A key holds at most 10 webhook destinations. Remove one to add another.
key_limit_reached409An account holds at most 3 test keys or 25 live keys that are not revoked. Revoke one to make another.
key_revoked409That key is revoked, so it cannot be rotated. Make a new key with POST /v1/keys.
key_rotation_conflict409Another 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_range409The 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.
no_webhook409This key has no webhook yet: set one with PUT /v1/account/webhook first.
webhook_conflict409The webhook changed while this request ran, so nothing was saved. Send the request again.
too_many_events413This run made more events than one delivery carries, so none were queued. Send fewer claims a call.
event_limit_reached429This 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.
webhook_rate_limited429Over this key's webhook deliveries a minute, so nothing was queued. Wait for the next minute.
queue_unavailable503We could not queue the events for delivery just now. Retry in a moment.

Code lists

The event types

Type, When it is sent
TypeWhen it is sent
pingSent when you ask for a test event, from the console or POST /v1/account/webhook/test. It carries no claim.
sandbox.file.acknowledgedThe 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.acknowledgedOne for each claim, from the 277CA: your reference, the payer claim number, accepted or rejected, and every fix as a sentence.
sandbox.remittance.receivedOne 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.updatedAn 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.liveThe payer confirmed the request: files of this kind may flow. It adds when, and the payer’s reference.
enrollment.rejectedThe request was refused. It adds the rejection: the payer’s words whole, and the remedy.
enrollment.canceledThe request was canceled, or you asked to cancel one that is with the payer or live (cancel_requested says which).
enrollment.endedA 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.assignedA task was added: its id, rank, who holds it, its title and its fields.
enrollment.task.completedA task was finished, by the provider, or by Payerworks for its own.
enrollment.task.deletedA task was replaced by another (superseded_by names it).
job.completedA 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.failedA batch job failed, or was stopped: its id, route, state, counts and the failure’s code. Nothing of the file you uploaded.
submission.file.acknowledgedThe 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.acknowledgedThe 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.receivedAn 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.

A delivery’s state

pending, scheduled, delivered, failed

When a destination’s delivery is tried again

After a failed attempt, a destination is tried again after 5 minutes, then 30 minutes, then 2 hours, then 8 hours, then 1 day, then 2 days: 7 attempts in all, then it is said failed, and you can send it again by hand.

Example

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

events.mjsJavaScript
// Payerworks example: events, signed, listed and sent again. Docs: https://payerworks.com/docs/events
//
// 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 events.mjs
// A test key and invented data only. Payerworks sends nothing to a payer: every file it writes is yours.
import { pathToFileURL } from 'node:url';
import { Payerworks, PayerworksError, verifyStandardWebhook } from './payerworks.mjs';
import { SENDER_ID, claimEndingIn, refusalOf } from './sample-data.mjs';

/**
 * On YOUR server, for each delivery: check the signature over the raw body, before you parse it.
 * `secret` is the whsec_ secret PUT /v1/account/webhook answered once; `headers` the request's headers.
 * A delivery may arrive more than once: skip a webhook-id you have handled.
 * @param {string} secret @param {Record<string, string>} headers @param {string} rawBody
 */
export function readDelivery(secret, headers, rawBody, seen = new Set()) {
  if (!verifyStandardWebhook(secret, headers, rawBody)) return { ok: false, reason: 'signature' };
  const id = headers['webhook-id'];
  if (seen.has(id)) return { ok: true, duplicate: true };
  seen.add(id);
  return { ok: true, event: JSON.parse(rawBody) };
}

/** @param {Payerworks} pw */
export async function run(pw) {
  // 1. Your webhook: an https address of yours. The signing secret is shown once: store it.
  const hook = await pw.putAccountWebhook({ url: 'https://hooks.example.com/payerworks' });

  // 2. A sandbox run makes events: the 999, each claim's 277CA, and each check's 835.
  const sandbox = await pw.postSandboxClaims({ sender_id: SENDER_ID, claims: [claimEndingIn('')] });

  // 3. Every event is kept for 30 days: list them, read one, and send one again.
  const listed = await pw.getEvents({ query: { limit: 10 } });
  const one = await pw.getEventsById(listed.data[0].id);
  const again = await pw.postEventsByIdRedeliver(one.id);

  // 4. The failure path: an id this key never had.
  const refused = await refusalOf(() => pw.getEventsById(`evt_${'0'.repeat(24)}`), { PayerworksError });

  return {
    secret_starts: hook.secret.slice(0, 6),
    made: sandbox.events.map((e) => e.type),
    listed: listed.data.length,
    redelivered: Boolean(again),
    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
{
  "secret_starts": "whsec_",
  "made": [
    "sandbox.file.acknowledged",
    "sandbox.claim.acknowledged",
    "sandbox.remittance.received"
  ],
  "listed": 3,
  "redelivered": true,
  "refused": {
    "status": 404,
    "code": "event_not_found",
    "request_id": "req_6"
  }
}

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.