Subscribe an endpoint to a business's domain events and Sessions delivers each one as a signed HTTP POST. Use them to sync registrations, purchases, and opt-ins into your own systems in near real time.
Create and manage endpoints in the business admin under Settings → Webhooks: add a URL, choose which events to receive, and reveal or rotate the signing secret.
Events
The event types you can subscribe to today. The key is what arrives in the payload's `type` field.
participant.marketing_consent_grantedA participant opted in to email marketing.
activity_registration.confirmedA participant's registration for an activity was confirmed.
product_purchase.completedA product purchase completed.
Payload
Every delivery is a JSON envelope. `id` is the event id — stable across retries, so you can dedupe on it.
{
"id": "evt_8f2c1a09b3",
"type": "participant.marketing_consent_granted",
"apiVersion": "2026-06-01",
"occurredAt": "2026-06-01T14:32:08.000Z",
"resourceReferences": [
"gid://Sessions/BusinessParticipant/p_91ac"
],
"data": {
"id": "gid://Sessions/BusinessParticipant/p_91ac",
"name": "Jordan Vega",
"email": "jordan@example.com",
"emailMarketingConsent": {
"status": "granted",
"at": "2026-06-01T14:32:08.000Z"
}
}
}`data` is the event's resource as a minimized node — the same field names and opaque GIDs the GraphQL API uses, with a reduced field set. Take the GID and re-query the API for the full object. `previousAttributes` is included only for events that change existing state.
Verifying signatures
Every request carries these headers:
Sessions-Signature: t=<unix-seconds>,v1=<hex HMAC-SHA256>
Sessions-Event-Type: activity_registration.confirmed
Sessions-Webhook-Id: <per-delivery id>The signature is HMAC-SHA256 over "<timestamp>.<rawBody>" using the endpoint's signing secret, hex-encoded. Recompute it over the exact bytes received and compare in constant time.
import {createHmac, timingSafeEqual} from 'node:crypto';
// Express example. `req.rawBody` must be the exact bytes we sent —
// verify before any JSON parsing re-serializes them.
function verify(req, signingSecret) {
const header = req.get('Sessions-Signature'); // "t=1717250000,v1=abc123…"
const parts = Object.fromEntries(
header.split(',').map((kv) => kv.split('=')),
);
const signed = `${parts.t}.${req.rawBody}`;
const expected = createHmac('sha256', signingSecret)
.update(signed)
.digest('hex');
const ok = timingSafeEqual(
Buffer.from(expected),
Buffer.from(parts.v1 ?? ''),
);
// Reject stale timestamps to bound replay (e.g. 5-minute tolerance).
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return ok && fresh;
}Retries & delivery
Respond with any 2xx status to acknowledge. A non-2xx, a timeout, or a network error is retried with escalating backoff (1m, 5m, 30m, 2h, 6h).
After six failed attempts a delivery is dead-lettered and no longer retried. Re-enable or fix the endpoint and future events will deliver normally.