Skip to content

Guides

Webhooks

Receive signed events when orders complete, attendees arrive and more.

Add an endpoint under Settings → Webhooks, choose the events, and copy the signing secret. Deliveries are JSON POST requests with these headers:

HeaderMeaning
EventOS-Signaturet=<unix seconds>,v1=<hex HMAC-SHA256 of "t.body" with your secret>
EventOS-EventThe event type, e.g. order.completed
EventOS-DeliveryUnique id of this delivery; the same event replayed keeps its payload id

Events

TypeWhen
order.completedA paid order is confirmed by the provider, or a free registration completes. Payload: order, attendees
order.cancelledAn organiser cancelled a free order
order.refundedA paid order was refunded in full
attendee.createdA named ticket was issued (checkout, organiser, API)
attendee.checked_inFirst entry scan
exhibitor.approvedAn exhibitor profile was approved
event.publishedAn event went live
pingTest from the Webhooks page

Verify the signature

Node.jsjavascript
import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(secret, rawBody, header) {
  const parts = Object.fromEntries(header.split(",").map((kv) => kv.split("=")));
  const age = Math.abs(Date.now() / 1000 - Number(parts.t));
  if (!parts.t || age > 300) return false;
  const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  return parts.v1?.length === expected.length && timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}
Pythonpython
import hmac, hashlib, time

def verify(secret: str, raw_body: bytes, header: str) -> bool:
    parts = dict(kv.split("=", 1) for kv in header.split(","))
    if abs(time.time() - int(parts["t"])) > 300:
        return False
    expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(parts.get("v1", ""), expected)

Delivery and retries

Respond with any 2xx within 10 seconds. Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours, then marked as given up. After twenty consecutive failures the endpoint is paused; re-enable it from the Webhooks page once fixed. Every delivery, its response and its attempts are shown there, and any delivery can be replayed.

Handle events idempotently: use the payload id to ignore duplicates. Replays and retries deliver the same id.
Webhooks · Developers · EventOS