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.
Every call but GET /v1/health sends a key as Authorization: Bearer pw_test_… or pw_live_…. A test key is free from the developers page, for invented data only, and writes test files only; a live key needs a signed business associate agreement first. Your key is a bearer key: keep it on your server, never in browser code.
Every answer carries x-request-id, and every refusal carries the same id as request_id inside the error. Quote it when you write to us. A POST, and PUT /v1/account/webhook, may carry an Idempotency-Key, unless its route says it takes none: a retry with the same key and request within 24 hours is answered as the first call was, by running it again, because Payerworks never keeps an answer.
Routes
- GET
/v1/healthHealth - GET
/v1/accountYour account - GET
/v1/account/contactYour notice address - PUT
/v1/account/contactSet your notice address - GET
/v1/keysYour account’s keys - POST
/v1/keysMake a key - DELETE
/v1/keys/{id}Revoke a key - POST
/v1/keys/{id}/rotateRotate a key - POST
/v1/mcpThe MCP server, hosted - GET
/v1/audit-logYour account’s audit log - GET
/v1/audit-log/exportDownload the audit log
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.
GET/v1/health
Health
Whether the API is answering. No key needed.
Request
curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/health"With the JavaScript client
const answer = await pw.getHealth();With the Python client
answer = pw.get_health(){
"ok": true,
"service": "Payerworks",
"version": "v1",
"transmits": false
}| Field | Type | What it is |
|---|---|---|
ok | boolean | |
service | string | |
version | string | |
transmits | the value false | No route transmits to a payer yet. |
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.
Request
curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/account" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY"With the JavaScript client
const answer = await pw.getAccount();With the Python client
answer = pw.get_account(){
"key_id": "pwk_0123456789abcdef",
"account": "Sample Billing Co",
"label": "Self-serve",
"mode": "test",
"rpm": 60,
"usage": {
"month": "2026-10",
"days": [
{
"date": "2026-10-18",
"calls": 12,
"refused": 0
},
{
"date": "2026-10-19",
"calls": 4,
"refused": 1
}
],
"total": 16,
"refused": 1
},
"webhook": {
"url": "https://hooks.example.com/payerworks/events",
"secret_last_four": "Osk=",
"created_at": "2026-10-19T15:00:00.000Z",
"previous_secret_expires_at": null,
"last_delivery": {
"at": "2026-10-19T15:00:00.000Z",
"trigger": "sandbox",
"first_event_id": "evt_cc264f7536a9315a0c6fe09f",
"events": 3,
"delivered": 3,
"failed": 0,
"status": 200,
"error": null
}
}
}| Field | Type | What it is |
|---|---|---|
key_id | string | |
account | string | |
label | string | |
mode | one of "test", "live" | |
rpm | integer | Calls a minute. |
usage | object | This month to date, by UTC day. |
webhook | object or null |
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.
Request
curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/account/contact" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY"With the JavaScript client
const answer = await pw.getAccountContact();With the Python client
answer = pw.get_account_contact(){
"email": "dev@example.com",
"updated_at": "2026-10-20T19:12:00.052Z"
}| Field | Type | What it is |
|---|---|---|
email | string or null | |
updated_at | string or null |
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.
Request
curl -sS -X PUT "$PAYERWORKS_BASE_URL/v1/account/contact" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.putAccountContact(body);With the Python client
answer = pw.put_account_contact(body){
"email": "dev@example.com"
}{
"email": "dev@example.com",
"updated_at": "2026-10-20T19:12:00.052Z"
}| Field | Type | What it is |
|---|---|---|
email | string or null | |
updated_at | string or null |
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.
Request
curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/keys" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY"With the JavaScript client
const answer = await pw.getKeys();With the Python client
answer = pw.get_keys(){
"account_id": "pwk_0123456789abcdef01",
"data": [
{
"key_id": "pwk_0123456789abcdef01",
"name": "Server",
"mode": "test",
"last_four": "aaaa",
"created_at": "2026-10-19T12:00:00.000Z",
"expires_at": null,
"expired": false,
"revoked_at": null,
"rotated_at": null,
"previous_secret_accepted_until": null,
"current": true
},
{
"key_id": "pwk_153493376889aa3239d7aab0",
"name": "CI runner",
"mode": "test",
"last_four": "khYc",
"created_at": "2026-10-20T19:12:00.100Z",
"expires_at": "2027-01-18T19:12:00.100Z",
"expired": false,
"revoked_at": null,
"rotated_at": null,
"previous_secret_accepted_until": null,
"current": false
}
]
}| Field | Type | What it is |
|---|---|---|
account_id | string | The account: the keys one key started. |
data | list of AccountKey |
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.
Request
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/keys" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.postKeys(body);With the Python client
answer = pw.post_keys(body){
"name": "CI runner",
"expires_in_days": 90
}{
"key_id": "pwk_153493376889aa3239d7aab0",
"name": "CI runner",
"mode": "test",
"last_four": "khYc",
"created_at": "2026-10-20T19:12:00.100Z",
"expires_at": "2027-01-18T19:12:00.100Z",
"expired": false,
"revoked_at": null,
"rotated_at": null,
"previous_secret_accepted_until": null,
"current": false,
"key": "pw_test_pBP8..."
}| Field | Type | What it is |
|---|---|---|
key_id | string | |
name | string | |
mode | one of "test", "live" | |
last_four | string | |
created_at | string | |
expires_at | string or null | |
expired | boolean | |
revoked_at | string or null | |
rotated_at | string or null | |
previous_secret_accepted_until | string or null | |
current | boolean | True for the key this call was made with. |
key | string | pw_test_ or pw_live_ and the secret: only in the answer that made it (POST /v1/keys, or a rotation). |
overlap_hours | integer |
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.
Request
curl -sS -X DELETE "$PAYERWORKS_BASE_URL/v1/keys/pwk_153493376889aa3239d7aab0" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY"With the JavaScript client
const answer = await pw.deleteKeysById("pwk_153493376889aa3239d7aab0");With the Python client
answer = pw.delete_keys_by_id("pwk_153493376889aa3239d7aab0"){
"key_id": "pwk_153493376889aa3239d7aab0",
"name": "CI runner",
"mode": "test",
"last_four": "6NXM",
"created_at": "2026-10-20T19:12:00.100Z",
"expires_at": "2027-01-18T19:12:00.100Z",
"expired": false,
"revoked_at": "2026-10-20T19:12:00.111Z",
"rotated_at": "2026-10-20T19:12:00.108Z",
"previous_secret_accepted_until": "2026-10-21T19:12:00.108Z",
"current": false
}| Field | Type | What it is |
|---|---|---|
key_id | string | |
name | string | |
mode | one of "test", "live" | |
last_four | string | |
created_at | string | |
expires_at | string or null | |
expired | boolean | |
revoked_at | string or null | |
rotated_at | string or null | |
previous_secret_accepted_until | string or null | |
current | boolean | True for the key this call was made with. |
key | string | pw_test_ or pw_live_ and the secret: only in the answer that made it (POST /v1/keys, or a rotation). |
overlap_hours | integer |
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.
Request
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/keys/pwk_153493376889aa3239d7aab0/rotate" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.postKeysByIdRotate("pwk_153493376889aa3239d7aab0", body);With the Python client
answer = pw.post_keys_by_id_rotate("pwk_153493376889aa3239d7aab0", body){
"overlap_hours": 24
}{
"key_id": "pwk_153493376889aa3239d7aab0",
"name": "CI runner",
"mode": "test",
"last_four": "6NXM",
"created_at": "2026-10-20T19:12:00.100Z",
"expires_at": "2027-01-18T19:12:00.100Z",
"expired": false,
"revoked_at": null,
"rotated_at": "2026-10-20T19:12:00.108Z",
"previous_secret_accepted_until": "2026-10-21T19:12:00.108Z",
"current": false,
"key": "pw_test_OOHH...",
"overlap_hours": 24
}| Field | Type | What it is |
|---|---|---|
key_id | string | |
name | string | |
mode | one of "test", "live" | |
last_four | string | |
created_at | string | |
expires_at | string or null | |
expired | boolean | |
revoked_at | string or null | |
rotated_at | string or null | |
previous_secret_accepted_until | string or null | |
current | boolean | True for the key this call was made with. |
key | string | pw_test_ or pw_live_ and the secret: only in the answer that made it (POST /v1/keys, or a rotation). |
overlap_hours | integer |
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.
Request
curl -sS -X POST "$PAYERWORKS_BASE_URL/v1/mcp" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY" \
-H "Content-Type: application/json" \
-d @request.jsonWith the JavaScript client
const answer = await pw.postMcp(body);With the Python client
answer = pw.post_mcp(body){
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": {
"name": "example-client",
"version": "1"
}
}
}{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-06-18",
"capabilities": {
"tools": {
"listChanged": false
},
"prompts": {
"listChanged": false
}
},
"serverInfo": {
"name": "payerworks",
"title": "Payerworks",
"version": "1.0.0"
},
"instructions": "Each tool calls one route of the Payerworks API with the key this connection presents. Payerworks validates claims, writes and reads X12 files and explains each answer in plain words. It transmits nothing: no file reaches a payer. post_sand…"
}
}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 |
|---|---|---|
jsonrpc | the value "2.0" | |
id | object | |
result | object | |
error | object | A JSON-RPC error: never the API’s error shape. |
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.
Request
curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/audit-log" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY"With the JavaScript client
const answer = await pw.getAuditLog();With the Python client
answer = pw.get_audit_log(){
"object": "list",
"data": [
{
"id": "aud_aeb4acb2fdf610d4bc531306",
"type": "webhook_destination.removed",
"words": "Webhook destination removed",
"at": "2026-10-20T19:12:00.114Z",
"actor": {
"type": "key",
"id": "pwk_0123456789abcdef01"
},
"door": "api",
"object": {
"kind": "webhook_destination",
"id": "wd_4e1687f5d8638cbd"
},
"changed": [],
"url_host": null,
"count": null,
"request_id": "req_example_15"
},
{
"id": "aud_a08c0b0cd9c2ce25b9ef59ab",
"type": "key.revoked",
"words": "API key revoked",
"at": "2026-10-20T19:12:00.111Z",
"actor": {
"type": "key",
"id": "pwk_0123456789abcdef01"
},
"door": "api",
"object": {
"kind": "key",
"id": "pwk_153493376889aa3239d7aab0"
},
"changed": [],
"url_host": null,
"count": null,
"request_id": "req_example_14"
}
],
"has_more": true,
"next_cursor": "acur_412331373932353233353230313038236175645f663663623031613433613161613366356130353463653832",
"retention_days": 365
}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 |
|---|---|---|
object | the value "list" | |
data | list of AuditEntry | Newest first. |
has_more | boolean | |
next_cursor | string or null | Pass as starting_after. |
retention_days | the value 365 | Each audit log entry is kept for 365 days (one year) from the moment of the change, then deleted. No entry is overwritten: each is written once, only where no entry of its id is. |
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.
Request
curl -sS -X GET "$PAYERWORKS_BASE_URL/v1/audit-log/export" \
-H "Authorization: Bearer $PAYERWORKS_API_KEY"With the JavaScript client
const answer = await pw.getAuditLogExport();With the Python client
answer = pw.get_audit_log_export(){
"object": "audit_log_export",
"content_type": "text/csv",
"filename": "payerworks-audit-log-2026-10-20.csv",
"row_count": 7,
"columns": [
"id",
"at"
],
"csv": "id,at,type,words,actor_type,actor_id,door,object_kind,object_id,changed,url_host,count,request_id\r\naud_aeb4acb2fdf610d4bc531306,2026-10-20T19:12:00.114Z,webhook_destination.removed,Webhook destination removed,key,pwk_0123456789abcdef01,api,…",
"has_more": false,
"next_cursor": null,
"retention_days": 365
}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 |
|---|---|---|
object | the value "audit_log_export" | |
content_type | the value "text/csv" | |
filename | string | |
row_count | integer | At most 5,000. |
columns | list of one of "id", "at", "type", "words", "actor_type", "actor_id", "door", "object_kind", "object_id", "changed", "url_host", "count", "request_id" | |
csv | string | The file, CRLF line ends; no cell opens as a formula in a spreadsheet. |
has_more | boolean | |
next_cursor | string or null | Pass as starting_after for the rest. |
retention_days | the value 365 | Each audit log entry is kept for 365 days (one year) from the moment of the change, then deleted. No entry is overwritten: each is written once, only where no entry of its id is. |
Reading the answer
- GET /v1/account answers the key’s rate a minute and its usage this month to date, by UTC day, with refused calls counted apart.
- A rotation (POST /v1/keys/{id}/rotate) keeps the key id, so webhooks, events, usage and enrollments carry on; the old secret is accepted only inside the overlap you choose. Every key of an account may manage its keys, unless the account’s organization chose that only its signed-in members do (see Organizations and members).
- GET /v1/audit-log answers who changed what on the account, when and through which door, by field name only, never a value; each entry is kept one year. GET /v1/audit-log/export gives the same entries as a CSV file.
Troubleshooting
invalid_key(401): The key is not valid, or was revoked: a revoked key stops within a minute.rate_limited(429): Over the key’s calls a minute. Wait the seconds retry-after names; the clients do this for you, at most twice.idempotency_key_reused(422): That Idempotency-Key was used in the last 24 hours for a different request. Make a new key for each new request.too_large(413): A request body is at most 2 MB. Larger work is split across calls.invalid_audit_type(400): type names an entry type (key.created) or its family (key).keys_managed_by_members(403): The account’s organization chose that only its signed-in members make, rotate and revoke keys: do it in the console, or ask an owner to switch it back.
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 |
|---|---|---|
invalid_audit_cursor | 400 | starting_after is the next_cursor of an earlier page of this audit log. |
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_concurrency | 400 | concurrency is a whole number from 1 to 10: how many deliveries this destination takes at once. |
invalid_contact_email | 400 | email is an address of at most 254 characters, or null to remove it. |
invalid_destination_id | 400 | A destination id is wd_ followed by 16 lowercase hexadecimal characters. |
invalid_destination_name | 400 | A destination's name is 1 to 80 characters. |
invalid_enabled | 400 | enabled is true or false, or left out. |
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_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. |
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. |
invalid_json | 400 | The body must be a JSON object. |
invalid_key_id | 400 | A key id is pwk_ followed by 16 to 40 letters and digits. |
invalid_key_name | 400 | A key's name is 1 to 80 characters. |
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. |
invalid_key | 401 | That key is not valid. Keys are issued by Payerworks; a revoked key stops within a minute. |
no_key | 401 | Send your API key as Authorization: Bearer <key>. |
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. |
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. |
destination_not_found | 404 | This key has no webhook destination with that id. |
key_not_found | 404 | This account has no key with that id. |
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. |
destination_conflict | 409 | The destination changed while this request ran, so nothing was saved. Send the request again. |
destination_disabled | 409 | This destination is disabled. Enable it with PUT /v1/webhook-destinations/{id} and enabled: true first. |
destination_limit_reached | 409 | A key holds at most 10 webhook destinations. Remove one to add another. |
idempotency_in_flight | 409 | The first request with this Idempotency-Key is still running. Retry after the seconds retry-after names. |
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. |
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. |
too_large | 413 | A request body may be at most 2 MB. |
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. |
rate_limited | 429 | Over this key's calls a minute. Wait the seconds retry-after names. |
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. |
Code lists
The headers on every answer
| Header | What it says |
|---|---|
x-request-id | The request’s id. Quote it when you write to Payerworks. |
x-ratelimit-limit | The key’s calls a minute. |
x-ratelimit-remaining | Calls left in this UTC minute. |
x-ratelimit-reset | Seconds until the window resets. |
RateLimit | The same figures in the IETF draft’s form. |
RateLimit-Policy | The key’s policy in the IETF draft’s form. |
idempotent-replayed | true when this is the first answer to an Idempotency-Key request, given again. |
retry-after | Seconds to wait before a retry. |
The audit log’s entry types
An entry names what changed by field name only, never a value: no name, address, note or answer is kept. A web address is kept as its host alone. Each audit log entry is kept for 365 days (one year) from the moment of the change, then deleted. No entry is overwritten: each is written once, only where no entry of its id is.
| Type | What changed | Fields it may name |
|---|---|---|
key.created | API key created | name, mode, rpm, expires_at |
key.rotated | API key secret rotated | secret, overlap_hours |
key.revoked | API key revoked | none |
key.expired | API key expired | none |
webhook.set | Account webhook set | url, secret |
webhook.changed | Account webhook changed | url, secret, overlap_hours |
webhook.removed | Account webhook removed | none |
webhook_destination.created | Webhook destination added | name, url, event_types, enabled, concurrency, secret |
webhook_destination.changed | Webhook destination changed | name, url, event_types, enabled, concurrency, secret, overlap_hours |
webhook_destination.removed | Webhook destination removed | none |
contact.changed | Notice contact set | email |
contact.removed | Notice contact removed | email |
provider.created | Provider added | name, npi, tax_id, tax_id_type, contacts |
provider.changed | Provider changed | name, npi, tax_id, tax_id_type, contacts |
provider.removed | Provider removed | none |
enrollment.created | Enrollment request created | provider_id, payer, transaction, primary_contact, notify_email, requested_effective_date, era_aggregation, ptan, note |
enrollment.changed | Enrollment request changed | provider_id, payer, transaction, primary_contact, notify_email, requested_effective_date, era_aggregation, ptan, note |
enrollment.submitted | Enrollment request submitted | none |
enrollment.withdrawn | Enrollment request withdrawn | reason |
enrollment.withdrawal_requested | Withdrawal of an enrollment request asked of Payerworks | reason |
enrollment.removed | Enrollment request deleted | none |
enrollment.status_changed | Enrollment request status changed | status |
enrollment.imported | Enrollment requests imported from a file | none |
enrollment.note_added | Note added to an enrollment request | none |
enrollment_task.answered | Enrollment task answered | values |
enrollment_task.added | Enrollment task added | none |
enrollment_task.superseded | Enrollment task replaced by another | none |
enrollment_task.completed | Enrollment task marked done | none |
enrollment_document.requested | Enrollment document upload started | none |
enrollment_document.added | Enrollment document added | none |
enrollment_document.removed | Enrollment document removed | none |
sftp_user.added | SFTP user added | acknowledgment_999 |
sftp_user.changed | SFTP user changed | acknowledgment_999 |
sftp_user.removed | SFTP user removed | none |
sftp_ssh_key.added | SFTP SSH key added | none |
sftp_ssh_key.removed | SFTP SSH key removed | none |
job.created | Batch job created | route |
job.canceled | Batch job canceled | none |
job.removed | Batch job and its files deleted | none |
account_baa.recorded | Business associate agreement recorded | signer_name, signer_title, organization, signed_on, baa_version, reference_prefix |
account_baa.withdrawn | Business associate agreement withdrawn | none |
org.created | Organization created | name |
org.updated | Organization changed | name, key_management |
member.invited | Member invited | email, role |
member.invitation_canceled | Member invitation canceled | none |
member.joined | Member joined | none |
member.role_changed | Member role changed | role |
member.removed | Member removed | keys_revoked |
Example
keys.mjs runs every step below against the API with a test key. It needs payerworks.mjs and sample-data.mjs beside it.
// Payerworks example: keys and your account. Docs: https://payerworks.com/docs/keys
//
// 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 keys.mjs
// A test key and invented data only. Payerworks sends nothing to a payer: every file it writes is yours.
import { pathToFileURL } from 'node:url';
import { Payerworks, PayerworksError } from './payerworks.mjs';
import { refusalOf } from './sample-data.mjs';
/** @param {Payerworks} pw */
export async function run(pw) {
// 1. Health needs no key, and says the API transmits nothing.
const health = await pw.getHealth();
// 2. Your key's rate a minute, and its calls this month to date by UTC day.
const account = await pw.getAccount();
// 3. A named key for one job, shown once, then revoked. Every key of an account may manage its keys,
// unless the account's organization chose that only its signed-in members do.
const made = await pw.postKeys({ name: 'Example job', expires_in_days: 1 });
const revoked = await pw.deleteKeysById(made.key_id);
// 4. The account's audit log: who made and revoked that key, when, and through which door, newest first.
const log = await pw.getAuditLog({ query: { limit: 2 } });
// 5. The failure path: a key id this account never had.
const refused = await refusalOf(() => pw.deleteKeysById('pwk_0000000000000000'), { PayerworksError });
return {
transmits: health.transmits,
mode: account.mode,
rpm: account.rpm,
made: { key_id: made.key_id.startsWith('pwk_'), shown_once: typeof made.key === 'string' },
revoked: Boolean(revoked),
audit: log.data.map((e) => ({ type: e.type, by: e.actor.type, door: e.door })),
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));
}
{
"transmits": false,
"mode": "test",
"rpm": 600,
"made": {
"key_id": true,
"shown_once": true
},
"revoked": true,
"audit": [
{
"type": "key.revoked",
"by": "key",
"door": "api"
},
{
"type": "key.created",
"by": "key",
"door": "api"
}
],
"refused": {
"status": 404,
"code": "key_not_found",
"request_id": "req_6"
}
}