# ROLLER Webhooks

ROLLER (venue and attraction ticketing) sends a JSON webhook when something happens, such as a
booking being created or cancelled. Webhooks are registered through the REST API. They are
**not signed**: when you register one you supply `authentication.apiKey`, and ROLLER sends that
value as plain text in the `x-roller-apikey` header of every delivery. Your endpoint
authenticates the request by comparing the header with the value you chose.

## When to Use This Skill

* How do I receive ROLLER webhooks?
* How do I verify the `x-roller-apikey` header?
* Does ROLLER sign webhook payloads? (No: it sends a shared API key)
* How do I handle booking created/cancelled or redemption events (`type` + `eventType`)?
* Why does my ROLLER webhook endpoint return 401?

## Setup note

Set the webhook's apiKey to a long random value, then paste the same value here.

"Here" is Hookdeck's ROLLER source, or the `ROLLER_WEBHOOK_API_KEY` variable in your own
handler. The apiKey is user-chosen and the docs' example is just a label, which would be
trivially guessable, so generate something like `openssl rand -hex 32`.

## Verification (core)

```javascript
const crypto = require('crypto');

// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
  const ha = crypto.createHash('sha256').update(String(a)).digest();
  const hb = crypto.createHash('sha256').update(String(b)).digest();
  return crypto.timingSafeEqual(ha, hb);
}

function verifyRollerRequest(headers, apiKey = process.env.ROLLER_WEBHOOK_API_KEY) {
  if (!apiKey) return false;                  // never accept when unconfigured
  const received = headers['x-roller-apikey']; // whole value, plain text
  return Boolean(received) && safeEqual(received, apiKey);
}

```

* Header: `x-roller-apikey: <apiKey>` (compare the **whole** value; name is case-insensitive).
* Return `401` on a missing header or mismatch, `400` for a body that isn't a JSON object, `200` once handled. Acknowledge within 10 seconds.
* Read the body raw (`express.raw({ type: '*/*' })`) and authenticate before parsing it.
* Python: `hmac.compare_digest` on **bytes** (encode both sides; `str` raises on non-ASCII).

**A static shared credential authenticates the sender, not the body.** There is no signature and
no timestamp, so there is no replay protection. Use HTTPS only, and rotate the apiKey
(re-register the webhook with a new value) if it leaks.

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/roller-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/roller-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/roller-webhooks/examples/fastapi/).

## Event Types

The JSON body carries the payload type in `type` (`Booking`, `Customer`, `Redemption`,
`BulkExport`, `SignedWaiver`, `PaymentLink`) and the action in `eventType` (`Created`,
`Updated`, `Cancelled`). ROLLER never sends a dotted name: the examples join the two fields
into the keys below. All webhooks are `POST`.

| Event (`type`.`eventType`) | Sent When |
| --- | --- |
| `Booking.Created` | A booking is created |
| `Booking.Updated` | A booking is changed |
| `Booking.Cancelled` | A booking is cancelled |
| `Customer.Created` | A guest is created (the guest webhook's `type` is `Customer`) |
| `Redemption.Created` | A ticket is redeemed |
| `PaymentLink.Created` | A payment link is created |

## Environment Variables

```bash
ROLLER_WEBHOOK_API_KEY=your-long-random-value   # the apiKey you set on the webhook

```

## Local Development

ROLLER can't reach `localhost`. Tunnel with the Hookdeck CLI (no account required):

```bash
npx hookdeck-cli listen 3000 roller --path /webhooks/roller

```

Use the printed URL as the webhook `url` when you register it.

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/roller-webhooks/references/overview.md) - What ROLLER webhooks are, events and payload shape
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/roller-webhooks/references/setup.md) - Register a webhook through the REST API
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/roller-webhooks/references/verification.md) - API key check details, gotchas, debugging 401s
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/roller-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests