Integrationer
Webhooks
Få besked inden for sekunder, når nogen tilmelder sig, betaler, kommer ind eller sletter sine data, i en body signeret efter Standard Webhooks, så du ved, at det var os.
Sådan virker de
Et webhook-endpoint er en https-adresse hos dig, som AttendBeacon kalder med en POST, når der sker noget ved dine events. Tilføj et i arbejdsområdet under Integrationer › Webhooks eller via API'et (POST /webhooks), vælg de hændelsestyper, det skal modtage, og gem den signeringshemmelighed, der vises én gang.
AttendBeacon skriver først hver hændelse i en kø i sin database og kalder derefter hvert endpoint, der lytter, så en genstart hos os aldrig mister en. Hvert kald har det samme besked-id ved hvert forsøg, så en gentagelse kan genkendes.
Hændelsestyperne
| Type | Sendes, når | Abonnement kræver |
|---|---|---|
attendee.registered | A registration was created. | attendees:read people:read |
attendee.updated | A registration was changed. | attendees:read people:read |
attendee.cancelled | A registration was cancelled. | attendees:read people:read |
attendee.erased | An attendee deleted their data. | attendees:read |
order.paid | An order was paid. | orders:read people:read |
checkin.created | Somebody was let in. | checkins:read people:read |
feedback.given | A rating was given. | surveys:read people:read |
lead.captured | A lead was captured. | sponsors:read people:read |
certificate.issued | A certificate was issued. | attendees:read |
content.published | Session slides were published. | program:read |
member.joined | Somebody joined a community. | communities:read |
member.left | A member left a community. | communities:read |
stay.requested | A hotel stay was asked for. | events:read |
stay.assigned | A hotel stay was assigned. | events:read |
stay.changed | A hotel stay changed. | events:read |
stay.cancelled | A hotel stay was cancelled. | events:read |
shift.signed_up | A volunteer took a shift. | crew:read |
shift.dropped | A volunteer came off a shift. | crew:read |
travel.changed | A guest's travel changed. | events:read |
claim.submitted | An expense claim was sent in. | finance:read |
claim.approved | An expense claim was approved. | finance:read |
claim.paid | An expense claim was paid. | finance:read |
Webhook-hændelser i referencen viser hver types felter og et eksempel. GET /webhooks/event-types svarer med den samme liste og hver payloads JSON-skema, til kode, der vil kontrollere det, den modtager.
Hvad der kommer frem
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
}
}
}Bodyen er { type, timestamp, data }. En deltager i data har de samme felter som den deltager, API'et svarer med, så kode skrevet til det ene virker til det andet. Typer om hotelophold, rejser, udlæg, vagter, deltagerbeviser, materialer og medlemmer af fællesskaber indeholder kun id'er, statusser, datoer og beløb, aldrig et navn.
Svar med en vilkårlig 2xx inden for 15 sekunder. Gør arbejdet bagefter: læg beskeden i din egen kø, og svar med det samme.
Kontrol af signaturen
Alle kan sende til din adresse, så kontrollér hvert kald, før du stoler på det. Signaturen er Base64-HMAC-SHA256 af {webhook-id}.{webhook-timestamp}.{body} med dit endpoints hemmelighed (delen efter whsec_, Base64-afkodet) som nøgle, skrevet som v1, efterfulgt af signaturen. Mens en hemmelighed udskiftes, kan headeren indeholde to, adskilt af et mellemrum; godkend kaldet, når én af dem passer.
- Kontrollér den rå body, præcis som den kom, før nogen JSON-parsing.
- Afvis et tidsstempel, der ligger mere end fem minutter fra dit ur, så et optaget kald ikke kan afspilles senere.
- Sammenlign i konstant tid.
- Husk de besked-id'er, du har behandlet, og spring en gentagelse over.
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));
});
}using System.Security.Cryptography;
using System.Text;
// secret: "whsec_..." as the workspace showed it; body: the raw request body as a string.
static bool Verify(string secret, string id, string timestamp, string signatures, string body)
{
if (!long.TryParse(timestamp, out var seconds) || Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - seconds) > 300) return false;
var key = Convert.FromBase64String(secret.StartsWith("whsec_") ? secret[6..] : secret);
var expected = Encoding.ASCII.GetBytes("v1," + Convert.ToBase64String(HMACSHA256.HashData(key, Encoding.UTF8.GetBytes($"{id}.{timestamp}.{body}"))));
return signatures.Split(' ', StringSplitOptions.RemoveEmptyEntries)
.Any(candidate => CryptographicOperations.FixedTimeEquals(Encoding.ASCII.GetBytes(candidate), expected));
}import base64, hashlib, hmac, time
# secret: "whsec_..." as the workspace showed it; body: the raw request body as bytes.
def verify(secret: str, headers: dict, body: bytes) -> bool:
msg_id = headers["webhook-id"]
timestamp = headers["webhook-timestamp"]
if abs(time.time() - int(timestamp)) > 300:
return False
key = base64.b64decode(secret.removeprefix("whsec_"))
signed = f"{msg_id}.{timestamp}.".encode() + body
expected = "v1," + base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode()
return any(hmac.compare_digest(candidate, expected) for candidate in headers["webhook-signature"].split(" "))# $secret: "whsec_..." as the workspace showed it; $body: the raw request body as a string.
function Test-AttendBeaconSignature($secret, $id, $timestamp, $signatures, $body) {
$now = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
if ([math]::Abs($now - [long]$timestamp) -gt 300) { return $false }
$hmac = [System.Security.Cryptography.HMACSHA256]::new([Convert]::FromBase64String($secret -replace '^whsec_', ''))
$hash = $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes("$id.$timestamp.$body"))
$expected = [Text.Encoding]::ASCII.GetBytes("v1," + [Convert]::ToBase64String($hash))
foreach ($candidate in $signatures -split ' ') {
if ([System.Security.Cryptography.CryptographicOperations]::FixedTimeEquals([Text.Encoding]::ASCII.GetBytes($candidate), $expected)) { return $true }
}
return $false
}Ordningen er Standard Webhooks, så dens biblioteker til dit sprog kontrollerer vores kald, som de er, med den samme hemmelighed.
Nye forsøg
Et kald, der ikke svarer 2xx inden for 15 sekunder, prøves igen, otte gange over lidt under et døgn: med det samme, efter 10 sekunder, et minut, 5 minutter, 30 minutter, 2 timer, 6 timer og 15 timer. Alle andre svar end 2xx prøves igen, også en 4xx: et endpoint, der svarer 401 efter en mislykket udrulning, er som regel rettet til næste morgen.
Undtagelsen er 410 Gone: det slår endpointet fra, fordi modtageren har sagt, at det ikke længere findes. Slå det til igen i arbejdsområdet, når det gør.
Hvert endpoints side i arbejdsområdet viser dets leveringer i 30 dage: hvad der blev sendt, hvert forsøg og hvad endpointet svarede, med Send igen til et, der mislykkedes. Send testevent sender en signeret webhook.test, mens du ser på.
Endpoints via API'et
Med webhooks:write kan en nøgle selv tilføje, ændre, teste og fjerne endpoints, hvilket passer til software, der sætter sine egne op: POST /webhooks svarer med endpointet og dets hemmelighed, én gang; POST /webhooks/{webhookId}/secret laver en ny og stopper den gamle med det samme. Et arbejdsområde har højst 20 endpoints.
Automatiseringsplatforme
Power Automate, Zapier og Make abonnerer i stedet via POST /hooks, ét abonnement pr. flow, og modtager de samme hændelser som almindelig JSON med data løftet op til øverste niveau: { id, type, timestamp, event, attendee }. De behøver ingen hemmelighed: den adresse, de opgiver, er selv deres hemmelighed. Se Power Automate og Zapier.