Skip to content

Webhook events

Understand the signed delivery envelope, occurrence identity, routing metadata, and all public event payloads.

Updated View as Markdown

Every delivery is an HTTPS POST with one stable business occurrence id and one transport-attempt id.

{
  "deliveryId": "d_91c3",
  "occurrenceId": "evtocc_8a2f",
  "event": "seat.booked",
  "at": 1761436800000,
  "payload": {
    "workspaceId": "ws_7e2d",
    "eventId": "ev_9f3a",
    "labels": ["A-1"],
    "bookingRef": "order_42",
    "livemode": true
  }
}
Header Value
Content-Type application/json
X-SeatLayer-Event Event name
X-SeatLayer-Signature sha256= plus the raw-body HMAC

Verify the signature before parsing or acting on the payload.

Identity and routing fields

Field Meaning
occurrenceId Stable business occurrence; idempotency key across retries and subscriptions
deliveryId One HTTP delivery attempt
at Occurrence time in epoch milliseconds
payload.workspaceId Immutable workspace boundary
payload.externalRef Optional host reconciliation tag
payload.environment Optional routing environment inherited from event creation
payload.livemode false for test events, otherwise true

Use workspaceId plus your stored mapping to route tenants. Never use externalRef alone as authorization.

Event catalog

Event Fires when Main payload fields
event.created Event materialization succeeds eventId, name, chartId, workspaceId, at
hold.created A hold is created or replaced eventId, labels, holdId, expiresAt, items, replaced?
hold.extended Active hold expiry moves forward eventId, holdId, expiresAt, extends
hold.expired Hold reaches expiry eventId, labels, holdId, items
seat.booked Buyer/server/POS booking succeeds eventId, labels, bookingRef, items, holdId?, at
seat.released Held, blocked, or booked inventory returns to free eventId, labels, holdId?, items?
seat.blocked Organizer blocks free inventory eventId, labels
event.soldout Last free inventory is booked for the first time eventId, at

event.soldout is guarded to emit once per event. A cancellation and later resale does not create a second sold-out occurrence.

Line items

Where present, items are server-generated inventory snapshots:

interface WebhookLineItem {
  label: string;
  objectId: string;
  objectType: "seat" | "booth" | "ga" | "table";
  categoryKey: string;
  tierId: string | null;
  unitPrice: number;
  currency: string;
  quantity: number;
}

Use them for order reconciliation and customer messaging. Your payment system remains authoritative for charges, captures, refunds, taxes, and settlement.

Replacement and release nuances

  • Replacing a hold emits hold.created with replaced: true and the complete new held set.
  • Labels removed during replacement also emit seat.released.
  • Expiry emits one hold.expired per hold and a release occurrence for freed labels.
  • Manual hold release, unblock, scheduled block release, and cancellation can all produce seat.released.
  • hold.extended carries no labels because inventory identity did not change.

Design handlers around the event name and ids rather than inferring the cause only from a seat status.

Minimal handler

server/webhooks.tsts
export async function handleSeatLayerWebhook(request: Request) {
  const rawBody = await request.text();
  verifySeatLayerSignature(
    rawBody,
    request.headers.get("x-seatlayer-signature"),
  );

  const message = JSON.parse(rawBody);

  if (await wasProcessed(message.occurrenceId)) {
    return new Response(null, { status: 204 });
  }

  switch (message.event) {
    case "seat.booked":
      await reconcileBooking(message.payload);
      break;
    case "seat.released":
      await reconcileRelease(message.payload);
      break;
    default:
      await recordUnhandledEvent(message);
  }

  await markProcessed(message.occurrenceId);
  return new Response(null, { status: 204 });
}

Persist the business mutation and processed occurrence id atomically when possible. Return 2xx for a known duplicate.

Continue to delivery and retries and signature verification.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close