Integraciones
Webhooks
Entérate en segundos cuando alguien se inscribe, paga, entra o elimina sus datos, con un cuerpo firmado según Standard Webhooks para que sepas que somos nosotros.
Cómo funcionan
Un punto de conexión de webhook es una dirección https tuya a la que AttendBeacon llama con un POST cuando pasa algo en tus eventos. Añade uno en el espacio de trabajo en Integraciones › Webhooks, o a través de la API (POST /webhooks), elige los tipos de evento que recibe y guarda el secreto de firma, que se muestra una sola vez.
AttendBeacon primero escribe cada evento en una cola de su base de datos y después llama a cada punto de conexión que escucha, así que un reinicio por nuestra parte nunca pierde ninguno. Cada llamada lleva el mismo id de mensaje en todos los intentos, para que se pueda reconocer una repetición.
Los tipos de evento
| Tipo | Se envía cuando | Para suscribirse se necesita |
|---|---|---|
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 |
Eventos de webhook en la referencia muestra los campos de cada tipo y un ejemplo. GET /webhooks/event-types devuelve la misma lista con el esquema JSON de cada carga útil, para el código que quiera comprobar lo que recibe.
Qué llega
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
}
}
}El cuerpo es { type, timestamp, data }. Un asistente en data tiene los mismos campos que el asistente que devuelve la API, así que el código escrito para uno sirve para el otro. Los tipos sobre estancias de hotel, viajes, notas de gastos, turnos, certificados, contenidos y miembros de comunidades solo llevan ids, estados, fechas e importes, nunca un nombre.
Responde con cualquier 2xx en menos de 15 segundos. Haz el trabajo después: pon el mensaje en una cola propia y responde de inmediato.
Cómo comprobar la firma
Cualquiera puede enviar un POST a tu dirección, así que comprueba cada llamada antes de fiarte de ella. La firma es el HMAC-SHA256 en Base64 de {webhook-id}.{webhook-timestamp}.{body} con el secreto de tu punto de conexión (la parte después de whsec_, decodificada en Base64), escrita como v1, seguido de la firma. Mientras se sustituye un secreto, la cabecera puede contener dos firmas separadas por un espacio; acepta la llamada si una coincide.
- Comprueba el cuerpo sin procesar, exactamente como llegó, antes de analizar el JSON.
- Rechaza una marca de tiempo que se aleje más de cinco minutos de tu reloj, para que una llamada grabada no se pueda reproducir más tarde.
- Compara en tiempo constante.
- Recuerda los ids de mensaje que ya has procesado y descarta las repeticiones.
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
}El esquema es Standard Webhooks, así que sus bibliotecas para tu lenguaje verifican nuestras llamadas tal cual, con el mismo secreto.
Reintentos
Una llamada que no responde 2xx en 15 segundos se vuelve a intentar ocho veces en algo menos de un día: de inmediato, a los 10 segundos, al minuto, a los 5 minutos, a los 30 minutos, a las 2 horas, a las 6 horas y a las 15 horas. Cualquier respuesta distinta de 2xx se reintenta, también un 4xx: un punto de conexión que responde 401 tras un despliegue fallido suele estar arreglado por la mañana.
La excepción es 410 Gone: desactiva el punto de conexión, porque el receptor ha dicho que ya no existe. Vuelve a activarlo en el espacio de trabajo cuando exista de nuevo.
La página de cada punto de conexión en el espacio de trabajo muestra sus entregas durante 30 días: qué se envió, cada intento y qué respondió el punto de conexión, con Reenviar para una que falló. Enviar evento de prueba envía un webhook.test firmado mientras lo observas.
Puntos de conexión a través de la API
Con webhooks:write, una clave puede añadir, cambiar, probar y eliminar puntos de conexión por sí misma, lo que viene bien para el software que configura los suyos: POST /webhooks devuelve el punto de conexión y su secreto, una sola vez; POST /webhooks/{webhookId}/secret crea uno nuevo y desactiva el antiguo de inmediato. Un espacio de trabajo tiene como máximo 20 puntos de conexión.
Plataformas de automatización
Power Automate, Zapier y Make se suscriben en cambio a través de POST /hooks, con una suscripción por flujo, y reciben los mismos eventos como JSON simple con data elevado al nivel superior: { id, type, timestamp, event, attendee }. No necesitan secreto: la dirección que dan es su propio secreto. Consulta Power Automate y Zapier.