AttendBeacon
Open the workspace

Integrations

Webhooks

Be told within seconds when somebody registers, pays, walks in or deletes their data, in a body signed the Standard Webhooks way so you know it was us.

How they work

A webhook endpoint is an https address of yours that AttendBeacon calls with a POST when something happens at your events. Add one in the workspace under Integrations › Webhooks, or through the API (POST /webhooks), choose the event types it receives, and keep the signing secret it shows once.

AttendBeacon writes each event to a queue in its database first, then calls every endpoint that listens, so a restart on our side never loses one. Each call carries the same message id on every attempt, so a repeat can be recognised.

The event types

TypeSent whenSubscribing needs
attendee.registeredA registration was created.attendees:read people:read
attendee.updatedA registration was changed.attendees:read people:read
attendee.cancelledA registration was cancelled.attendees:read people:read
attendee.erasedAn attendee deleted their data.attendees:read
order.paidAn order was paid.orders:read people:read
checkin.createdSomebody was let in.checkins:read people:read
feedback.givenA rating was given.surveys:read people:read
lead.capturedA lead was captured.sponsors:read people:read
certificate.issuedA certificate was issued.attendees:read
content.publishedSession slides were published.program:read
member.joinedSomebody joined a community.communities:read
member.leftA member left a community.communities:read
stay.requestedA hotel stay was asked for.events:read
stay.assignedA hotel stay was assigned.events:read
stay.changedA hotel stay changed.events:read
stay.cancelledA hotel stay was cancelled.events:read
shift.signed_upA volunteer took a shift.crew:read
shift.droppedA volunteer came off a shift.crew:read
travel.changedA guest's travel changed.events:read
claim.submittedAn expense claim was sent in.finance:read
claim.approvedAn expense claim was approved.finance:read
claim.paidAn expense claim was paid.finance:read

Webhook events in the reference shows each type's fields and an example. GET /webhooks/event-types answers the same list with each payload's JSON schema, for code that wants to check what it receives.

What arrives

HTTP
POST /attendbeacon HTTP/1.1
Host: hooks.example.com
Content-Type: application/json
webhook-id: msg_2f6c1a9e4b7d4c0f8e3a5b9d1c7e0f42
webhook-timestamp: 1792130400
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=

{
  "type": "attendee.registered",
  "timestamp": "2026-10-14T12:00:00.000Z",
  "data": {
    "event": { "eventId": "b3ac0000-0000-4000-8000-000000000001", "slug": "beacon-summit", "name": "Beacon Summit" },
    "attendee": {
      "attendeeId": "b3ac0008-0000-4000-8000-000000000000",
      "ticketTypeName": "Conference pass",
      "firstName": "Emma",
      "lastName": "Bakker",
      "email": "emma.bakker0@example.com",
      "status": "active",
      "valid": true
    }
  }
}

The body is { type, timestamp, data }. An attendee in data has the same fields as the attendee the API answers, so code written for one works for the other. Types about hotel stays, travel, claims, shifts, certificates, content and community members carry ids, statuses, dates and amounts only, never a name.

Answer with any 2xx within 15 seconds. Do the work afterwards: put the message on a queue of your own and answer at once.

Checking the signature

Anybody can post to your address, so check every call before you trust it. The signature is the Base64 HMAC-SHA256 of {webhook-id}.{webhook-timestamp}.{body} under your endpoint's secret (the part after whsec_, Base64-decoded), written v1, and the signature. While a secret is being replaced the header may hold two, separated by a space; accept the call when one matches.

  • Check the raw body, exactly as it arrived, before any JSON parsing.
  • Refuse a timestamp more than five minutes from your clock, so a recorded call cannot be replayed later.
  • Compare in constant time.
  • Remember message ids you have handled, and skip a repeat.
import { createHmac, timingSafeEqual } from "node:crypto";

// secret: "whsec_..." as the workspace showed it; body: the raw request body as a string.
export function verify(secret, headers, body) {
  const id = headers["webhook-id"];
  const timestamp = Number(headers["webhook-timestamp"]);
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false;

  const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
  const expected = createHmac("sha256", key).update(`${id}.${timestamp}.${body}`).digest("base64");

  return headers["webhook-signature"].split(" ").some((candidate) => {
    const [version, signature] = candidate.split(",");
    return version === "v1" && signature.length === expected.length
      && timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
  });
}

The scheme is Standard Webhooks, so its libraries for your language verify our calls as they are, with the same secret.

Retries

A call that does not answer 2xx within 15 seconds is tried again, eight times over a little under a day: at once, after 10 seconds, a minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 15 hours. Every answer other than 2xx is retried, a 4xx too: an endpoint that answers 401 after a bad deploy is usually fixed by the morning.

The exception is 410 Gone: it switches the endpoint off, because the receiver said it no longer exists. Switch it on again in the workspace when it does.

Each endpoint's page in the workspace lists its deliveries for 30 days: what was sent, every attempt and what the endpoint answered, with Resend for one that failed. Send test sends a signed webhook.test while you watch.

Endpoints through the API

With webhooks:write a key can add, change, test and remove endpoints itself, which suits software that sets up its own: POST /webhooks answers the endpoint and its secret, once; POST /webhooks/{webhookId}/secret makes a new one and stops the old one at once. A workspace has at most 20 endpoints.

Automation platforms

Power Automate, Zapier and Make subscribe through POST /hooks instead, one subscription per flow, and receive the same events as plain JSON with data lifted to the top: { id, type, timestamp, event, attendee }. They need no secret: the address they give is their own secret. See Power Automate and Zapier.

The guides and this site are in your language. The API reference's own descriptions are in English, as the API itself speaks.