Skip to main content
Webhook management is server-side. Register and delete endpoints with the Org-API-Key from your backend, and verify deliveries on a backend HTTPS endpoint. Never manage webhooks from an app.
Webhooks push lifecycle events from Expys to an HTTPS endpoint you control, so your systems react to redemptions, point changes, member changes, and concierge messages without polling.

Register an endpoint

Create an endpoint with the events you want. The response includes the signing secret once - store it immediately; it is never returned again.
string
required
Endpoint id (use it to delete the endpoint).
string
required
Your HTTPS delivery URL.
string[]
required
The subscribed event names.
string
required
SANDBOX or LIVE, from the key you used.
string
required
The HMAC signing secret, prefixed whsec_. Shown once - store it now.
string
required
ISO-8601 creation time.
Manage endpoints with GET /v1/webhooks (list) and DELETE /v1/webhooks/{id}. An org may hold up to 10 active endpoints per environment. See the API reference.

Event catalog

The redemption.* names cover the full booking lifecycle - one event per status transition - so a CRM sees the whole course of an experience.
Subscribing to an event name not in this catalog is rejected with WEBHOOK_EVENT_UNKNOWN. A non-HTTPS or disallowed URL is rejected with WEBHOOK_URL_NOT_ALLOWED.

Delivery format

Each delivery is a POST with a JSON body and these headers:

Verify the signature

Recompute HMAC-SHA256 over the exact raw request body (not a re-serialized object) with your signing secret, hex-encode it, prefix sha256=, and compare in constant time against X-Expys-Signature.
Read the raw body bytes before any JSON parsing middleware reshapes them. Verifying against a re-serialized object will fail because key order and whitespace differ from what was signed.

Retries and dead-lettering

A delivery is retried on any non-2xx response or timeout, with exponential backoff: first retry after 30s, doubling each time, capped at 1 hour. After 6 total attempts the delivery is dead-lettered and no longer retried.
Each delivery request times out after 10 seconds, so respond quickly. Do the real work asynchronously - acknowledge with a 2xx as soon as you have verified the signature and enqueued the event.

Consume idempotently

Deliveries are at-least-once: a retry can arrive after you already processed an event. Deduplicate on X-Expys-Delivery (or the event’s own id) and make handlers idempotent so a duplicate is a no-op.