# Following an event

> A webhook set, a sandbox run that makes events, each signature checked on your own server, and the event log read back.

Page: https://payerworks.com/docs/quickstart-events · Markdown: https://payerworks.com/docs/quickstart-events.md

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

## What you need

- A test key, from [the developers page](https://payerworks.com/developers). It is for invented data only.
- Node 18 or later, the JavaScript client [payerworks.mjs](https://payerworks.com/sdk/payerworks.mjs), and [sample-data.mjs](https://payerworks.com/docs/examples/sample-data.mjs), the invented patient and clinic every example uses, in one folder.
- Two environment variables: `PAYERWORKS_API_KEY` (your key) and `PAYERWORKS_BASE_URL` (the base URL shown with your key).

## The steps

1. Set your webhook with PUT /v1/account/webhook: an https address at a public hostname. The answer shows its signing secret once; store it.
2. Run the claims sandbox. Each answer is recorded as an event on your account and posted to your webhook: the 999, each claim’s 277CA, and each check’s 835.
3. On your server, check each delivery’s signature over the raw body before you parse it (`readDelivery` in the example), and skip a webhook-id you have already handled: a delivery may arrive more than once.
4. List what was recorded with GET /v1/events, read one with GET /v1/events/{id}, and send one again with POST /v1/events/{id}/redeliver. Events are kept for 30 days.

## The code

[events.mjs](https://payerworks.com/docs/examples/events.mjs) runs every step below against the API with a test key. It needs [payerworks.mjs](https://payerworks.com/sdk/payerworks.mjs) and [sample-data.mjs](https://payerworks.com/docs/examples/sample-data.mjs) beside it.

events.mjs

```js
// 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"
  }
}
```

## What you should see

- `made` lists the three event types one paid claim makes; `refused` is an event id the key never had.

## Next

- Add more destinations, each with its own event types: [Events and webhooks](https://payerworks.com/docs/events).
