Guide to ROLLER Webhooks: Features and Best Practices
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
| Feature | Details |
|---|---|
| Registration | REST API only: POST /webhooks, plus view, update and delete endpoints |
| Activation | enabled defaults to false. Set it to true |
| Endpoint | HTTPS |
| Method | POST |
| Envelope | id, sendDate, type, eventType, eventDate, data |
| Authentication | x-roller-apikey: <authentication.apiKey>. No signature |
| Response window | 10 seconds maximum, under 1 second ideally |
| Success | 200 OK |
| Retries | Seven attempts over about four days for failures and 500, 503 and 504 responses |
| Other status codes | Trigger a review and possible deactivation |
| Ordering | Not guaranteed |
| Typical latency | 2 to 5 seconds after the event |
| Source IPs | Published per region (US, APAC, EMEA), optional allowlist |
| API usage | Managing webhooks counts toward your API package limit; receiving them does not |
Common events
type | eventType | Sent when |
|---|---|---|
Booking | Created | A reserved booking is created (draft bookings are not included) |
Booking | Updated | A booking changes: items, contacts, payments, refunds, discounts, ticket details and more |
Booking | Cancelled | A booking is cancelled |
Customer | Created | A guest is created |
Customer | Updated | A guest is updated |
Redemption | Created | A ticket is redeemed |
BulkExport | Created | An export has finished and its files are ready to download |
SignedWaiver | Created | An adult waiver, with any associated minors, is signed |
PaymentLink | Created | A payment link is created |
PaymentLink | Updated | A payment is recorded against a payment link |
PaymentLink | Cancelled | A 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
datayour 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-apikeyheader, answers ROLLER with a200in 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
200for every genuine ROLLER request you have stored, even if processing fails later. - Return
503for 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
eventDateto 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.