Webhooks

Create a webhook in Settings → Webhooks or via POST /api/v1/webhooks. Deliveries are JSON POSTs, signed, retried five times with exponential backoff, and every attempt is visible in the deliveries log.

Events

booth.created
booth.updated
booth.deleted
booth.status_changed
booth.assigned
booth.unassigned
booth.on_hold
exhibitor.created
exhibitor.updated
exhibitor.deleted
order.created
order.paid
order.cancelled
order.expired
floorplan.published
session.created
session.updated
session.deleted
extra.assigned
extra.removed

Payload

{
  "id": "wd_…", "type": "booth.status_changed", "createdAt": "2026-09-14T10:00:00.000Z", "eventId": "ev_…",
  "data": { "booth": { "id": "bo_…", "label": "A101", "status": "sold", "exhibitorIds": ["ex_…"], … }, "previousStatus": "held" }
}

Verify the signature

import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(",").map((kv) => kv.split("=")));
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return timingSafeEqual(Buffer.from(v1, "hex"), Buffer.from(expected, "hex")) && Math.abs(Date.now() / 1000 - Number(t)) < 300;
}
// headers: X-Tessera-Signature: t=1726300000,v1=…   X-Tessera-Event: booth.status_changed   X-Tessera-Delivery: wd_…

ExpoFP integrations that expect booth_reserved/booth_assigned/exhibitor_upserted map onto booth.status_changed, booth.assigned and exhibitor.updated; the payload carries our ids plus externalId.