Gareth Wilson Gareth Wilson

Guide to ROLLER Webhooks: Features and Best Practices

Published


ROLLER webhooks tell your systems what is happening at your venue as it happens: a booking is made, changed or cancelled, a guest record is created, a ticket is scanned at the gate, a waiver is signed, a payment link is paid, or a bulk export finishes. If you run a trampoline park, water park, museum or other attraction on ROLLER, webhooks are how bookings and redemptions reach your CRM, marketing tools and reporting without polling the API.

This guide covers how ROLLER webhooks are registered and authenticated, the message envelope and event types, ROLLER's strict response rules and retry schedule, and the practices that keep a webhook from being disabled.

What are ROLLER webhooks?

ROLLER webhooks are managed entirely through the REST API. You register a webhook with an HTTPS URL, an apiKey of your choosing, and the webhook types and events you want, such as booking Created and Cancelled. ROLLER then sends an HTTP POST to the URL each time one of those events happens.

Every message uses the same envelope. type says which webhook it is (Booking, Customer, Redemption and so on) and eventType says what happened (Created, Updated or Cancelled). The record itself, such as the booking, is in data.

ROLLER doesn't sign webhooks. The authentication.apiKey you set when you create the webhook comes back as plain text in the x-roller-apikey header of every delivery. Comparing that header with the value you chose is how your receiver tells a ROLLER request from a forged one.

ROLLER webhook features

FeatureDetails
RegistrationREST API only: POST /webhooks, plus view, update and delete endpoints
Activationenabled defaults to false. Set it to true
EndpointHTTPS
MethodPOST
Envelopeid, sendDate, type, eventType, eventDate, data
Authenticationx-roller-apikey: <authentication.apiKey>. No signature
Response window10 seconds maximum, under 1 second ideally
Success200 OK
RetriesSeven attempts over about four days for failures and 500, 503 and 504 responses
Other status codesTrigger a review and possible deactivation
OrderingNot guaranteed
Typical latency2 to 5 seconds after the event
Source IPsPublished per region (US, APAC, EMEA), optional allowlist
API usageManaging webhooks counts toward your API package limit; receiving them does not

Common events

typeeventTypeSent when
BookingCreatedA reserved booking is created (draft bookings are not included)
BookingUpdatedA booking changes: items, contacts, payments, refunds, discounts, ticket details and more
BookingCancelledA booking is cancelled
CustomerCreatedA guest is created
CustomerUpdatedA guest is updated
RedemptionCreatedA ticket is redeemed
BulkExportCreatedAn export has finished and its files are ready to download
SignedWaiverCreatedAn adult waiver, with any associated minors, is signed
PaymentLinkCreatedA payment link is created
PaymentLinkUpdatedA payment is recorded against a payment link
PaymentLinkCancelledA payment link is cancelled

ROLLER never sends a combined name such as Booking.Created. Route on the pair of type and eventType. The guest webhook's type is Customer, not Guest. The envelope's eventType enum also lists Deleted, though no webhook type documents sending it, so log it if it ever turns up.

Several events have rules worth knowing before you build on them. ROLLER POS batches redemptions every 15 seconds, so one Redemption message can carry several tickets, and redeeming stock and add-on products sends no webhook at all. Creating a booking does not also fire Booking Updated, and neither do redeeming tickets or editing the guest record attached to a booking.

The envelope looks like this:

{
  "id": "67d2ada6-10cc-4177-909d-430f3b2593d4",
  "sendDate": "2024-06-14T05:30:48.1597733Z",
  "type": "Booking",
  "eventType": "Created",
  "eventDate": "2024-06-14T05:30:48.1132441Z",
  "data": {
    "...": "the booking payload"
  }
}

The id, sendDate and eventDate values come from ROLLER's schema. ROLLER publishes the models for data (such as the booking payload) but not a complete delivered example. Optional fields in data, such as tickets, payments or membership details, appear only if you turn on the matching include flag when you register the webhook.

Inspect ROLLER webhooks as they arrive. Register a webhook against a Hookdeck Inspect URL to see the exact envelope and data your venue sends, with no account or setup required.

Setting up ROLLER webhooks

Generate a long random value for the API key first, for example with openssl rand -hex 32. ROLLER's own example value is a readable label (CRM Trigger Webhook 002), which anyone could guess.

Register the webhook with the REST API, using an access token for your venue:

curl --request POST \
  --url https://api.roller.app/webhooks \
  --header "Authorization: Bearer $ROLLER_ACCESS_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{
    "url": "https://example.com/webhooks/roller",
    "enabled": true,
    "authentication": { "apiKey": "YOUR_LONG_RANDOM_VALUE" },
    "webhooks": {
      "booking": { "events": ["Created", "Updated", "Cancelled"] },
      "redemption": { "events": ["Created"] }
    }
  }'

The access token comes from ROLLER's OAuth client credentials flow. Use https://api.play.roller.app instead to test against a ROLLER Playground venue. enabled must be true or the webhook stays inactive, and at least one webhook type needs an events list. Each type also takes optional include flags for extra fields and filter objects, for example to limit booking webhooks to certain products, sales channels or the application that triggered the change.

Deleting or disabling the API key you used to register a webhook does not delete or disable the webhook. Use the delete endpoint when you retire an integration.

To route through Hookdeck, create a source with the ROLLER type and use its URL as url. The source has one field, API Key. As Hookdeck's setup note says, set the webhook's apiKey to a long random value, then paste the same value there. Hookdeck compares it with the x-roller-apikey header and accepts POST.

Securing ROLLER webhooks

Compare x-roller-apikey with your stored value, in constant time. ROLLER adds no prefix, so compare the whole value.

const crypto = require('crypto');
const express = require('express');

const API_KEY = process.env.ROLLER_WEBHOOK_API_KEY;

function safeEqual(received, expected) {
  const a = Buffer.from(received, 'utf8');
  const b = Buffer.from(expected, 'utf8');
  // timingSafeEqual throws on unequal lengths, so check first.
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

const app = express();

app.post('/webhooks/roller', express.raw({ type: '*/*' }), (req, res) => {
  const received = req.get('x-roller-apikey');
  if (!API_KEY || typeof received !== 'string' || !safeEqual(received, API_KEY)) {
    // Not a ROLLER request. ROLLER itself never sees this response.
    return res.sendStatus(401);
  }

  let message;
  try {
    message = JSON.parse(req.body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }

  // Store message.id, queue the work keyed on `${message.type}.${message.eventType}`,
  // and answer well inside 10 seconds.
  res.sendStatus(200);
});

The same check in Python:

import hmac
import os

API_KEY = os.environ.get("ROLLER_WEBHOOK_API_KEY", "")


def verify_roller_request(headers) -> bool:
    if not API_KEY:
        return False  # never accept when unconfigured
    received = headers.get("x-roller-apikey")
    if not received:
        return False
    # Compare bytes: compare_digest raises TypeError on a non-ASCII str.
    return hmac.compare_digest(received.encode("utf-8"), API_KEY.encode("utf-8"))

A static API key authenticates the sender, not the body. It cannot detect a modified payload, and with no signature or timestamp a captured request can be replayed and still pass. Use a long random key, keep the HTTPS endpoint ROLLER requires, and rotate the key by updating the webhook if it might have leaked. ROLLER's published egress IPs give you a second, optional check at your firewall.

Make ROLLER webhooks production-ready. Hookdeck Event Gateway checks the x-roller-apikey header, answers ROLLER with a 200 in well under a second, and queues every booking and redemption durably for your service.

ROLLER webhook limitations and pain points

The wrong status code can switch a webhook off

The Problem: Your endpoint returns 401, 404 or 422 during a deploy or because of a bug, and ROLLER flags the webhook for deactivation instead of retrying.

Why It Happens: ROLLER accepts 200 as success and retries only on 500, 503 and 504. It says any other status code "will trigger an automatic review and potential deactivation of the webhook".

Workarounds:

  • Return 200 for every genuine ROLLER request you have stored, even if processing fails later.
  • Return 503 for temporary problems on your side, so ROLLER retries.

How Hookdeck Can Help: Hookdeck answers ROLLER with a 200 as soon as a request is stored, so your service's status codes never reach ROLLER. Failed deliveries to your service are retried by Hookdeck instead.

Ten seconds, and a webhook that dies after four days

The Problem: A slow handler causes retries, and a sustained outage disables the webhook altogether.

Why It Happens: ROLLER wants a response within 10 seconds, ideally under 1. Failed deliveries are retried immediately, then after 1 minute, 1 minute, 15 minutes, 1 hour, 1 day and 3 days. The venue's account owner and primary contact are alerted at the sixth attempt, and after the seventh the webhook is disabled.

Workarounds:

  • Store the message and respond, then process it in a background job, as ROLLER recommends.
  • Make sure the venue's account owner and primary contact in ROLLER are people who will act on the alert.

How Hookdeck Can Help: Hookdeck's ingestion stays up when your service doesn't, so ROLLER sees successful deliveries and never reaches its deactivation threshold. Issues alert you to failing deliveries on your side.

Events arrive out of order

The Problem: A redemption arrives before the Booking Created it belongs to.

Why It Happens: ROLLER does not guarantee delivery order, and retries make late arrivals more likely.

Workarounds:

  • Use eventDate to decide which change is newest before applying it.
  • Fetch missing objects from the API when an event refers to something you haven't seen yet, as ROLLER suggests.

How Hookdeck Can Help: Every message is stored in Hookdeck with its arrival time, so you can see what arrived when and replay events after a fix.

A guessable key

The Problem: The x-roller-apikey value is a readable name, such as the one in ROLLER's example, and anyone who guesses it can send fake bookings.

Why It Happens: authentication.apiKey is described as "a value used to identify this webhook", so it is easy to treat as a label.

Workarounds:

  • Use a long random value and store it like a password.
  • Add ROLLER's regional egress IPs to your allowlist as a second check.

How Hookdeck Can Help: Hookdeck's ROLLER source compares the header with the value you paste. Hookdeck still answers with a 200, but a request with a missing or wrong key is recorded as failed verification and never reaches your service.

Best practices

Route on type and eventType together

Neither field identifies the event alone. Build a key such as Booking.Cancelled in your own code and acknowledge pairs you don't handle.

Deduplicate on id

Each message has a unique id. Store it before processing and skip repeats. See our guide to webhook idempotency.

Expect batched redemptions

A Redemption message can describe several tickets redeemed in the same 15-second window at POS. Process every ticket in data.

Ask only for the fields you need

The include flags add fields such as tickets, payments and membership details to data. Turning on only what you use keeps payloads small.

Acknowledge fast and process asynchronously

ROLLER's 1-second target leaves no room for work in the request path. See why to process webhooks asynchronously.

Conclusion

ROLLER sends each webhook as a JSON POST with the apiKey you registered in the x-roller-apikey header and no signature. Choose a long random key, compare it in constant time, route on type plus eventType, deduplicate on id, and answer with 200 well inside 10 seconds, because ROLLER disables webhooks that keep failing and reviews any that return unexpected status codes.

Hookdeck Event Gateway checks the key, answers ROLLER immediately, and keeps every event until your service has processed it.

Get started with Hookdeck for free and handle ROLLER webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.