Avisos firmados cuando pasa algo en tu workspace.
Cada evento llega a tu servidor como un POST con JSON, firmado con Standard Webhooks. Los 13 eventos del catálogo se emiten hoy.
Lo que recibe tu servidor.
Una petición POST con cuatro cosas: la firma, la marca de tiempo, el nombre del evento y un cuerpo JSON con id, event, createdAt y data.
POST /tu/endpoint HTTP/1.1
content-type: application/json
user-agent: BannerMotion-Webhooks/1
webhook-id: evt_p0q8w2e6r4t1
webhook-timestamp: 1759382964
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
x-bm-event: banner.generated
x-bm-delivery: dlv_k29x8m1q4a7z
x-bm-signature: sha256=3b1f... # de compatibilidad
{
"id": "evt_p0q8w2e6r4t1",
"event": "banner.generated",
"createdAt": "2025-10-02T05:29:24.100Z",
"data": {
"jobId": "job_x4k2m9q1a7bz",
"campaignId": "cmp_8f2a1c3d4e5f",
"creativeId": "crt_3a9c0d1e2f4b",
"versionNo": 1,
"source": "api"
}
}| Encabezado | Qué es |
|---|---|
| webhook-id | Id del evento (evt_…), el mismo del cuerpo. No cambia en los reintentos. |
| webhook-timestamp | Segundos Unix del envío. |
| webhook-signature | Standard Webhooks: v1,<base64 del HMAC-SHA256>. Dos firmas separadas por un espacio durante una rotación. |
| x-bm-event | El nombre del evento, por ejemplo banner.generated. |
| x-bm-delivery | El id de esta entrega (dlv_…). |
| x-bm-signature | Firma anterior, por compatibilidad: sha256=<hex>. |
En Integraciones y API pulsa Agregar endpoint, escribe la dirección de tu servidor, marca los eventos y pulsa Crear endpoint. Copia el secreto (whsec_…): solo se muestra una vez.
En el menú del endpoint, Enviar evento de prueba: recibes un webhook.test firmado. No cuenta para la pausa automática.
webhook-id es el id del evento (evt_…) y también va en el cuerpo. En los reintentos de una entrega no cambia: úsalo para descartar repetidos.
Los 13 eventos, con su carga de ejemplo.
Elige un evento para ver cuándo se emite y qué trae data. Todos se emiten hoy; puedes suscribirte a los que quieras.
banner.generated
Un concepto terminó de generarse, con todos sus tamaños e idiomas. Lo emiten las generaciones desde la API y desde la app.
Sale de generaciones pedidas por la API y de las hechas en la aplicación.
| Campo | Qué es |
|---|---|
| jobId | Trabajo que generó el concepto (job_…). |
| campaignId | La campaña (cmp_…). |
| creativeId | El concepto generado (crt_…). Léelo con GET /api/v1/banners/:id. |
| versionNo | Número de versión. Solo viene cuando el concepto se pidió por la API. |
| source | "api" si lo pediste por la API; "app" si se generó en la aplicación. |
{
"id": "evt_p0q8w2e6r4t1",
"event": "banner.generated",
"createdAt": "2026-01-01T12:00:00.000Z",
"data": {
"jobId": "job_x4k2m9q1a7bz",
"campaignId": "cmp_8f2a1c3d4e5f",
"creativeId": "crt_3a9c0d1e2f4b",
"versionNo": 1,
"source": "api"
}
}Comprueba que el aviso es de BannerMotion.
Firmamos con Standard Webhooks, así que sirve cualquiera de sus bibliotecas. Los SDK traen verifyWebhook / verify_webhook listo.
- Usa el cuerpo exactoEl texto tal como llegó, antes de convertirlo a JSON.
- Arma el contenido firmado
webhook-id.webhook-timestamp.cuerpo - Calcula el HMAC-SHA256La clave son los bytes del base64 que sigue a
whsec_en tu secreto. El resultado va en base64, con el prefijov1,. - Compara en tiempo constanteCon cada firma
v1,…del encabezado. Durante una rotación del secreto llegan dos, separadas por un espacio. - Rechaza lo viejo y lo repetidoMarcas de tiempo a más de
5minutos del reloj, y eventos que ya procesaste (porid).
import { verifyWebhook } from "@bannermotion/sdk";
// rawBody: el cuerpo EXACTO recibido, como texto (antes de convertirlo a JSON).
// Durante una rotación del secreto pasa los dos: [nuevo, anterior].
const event = await verifyWebhook(process.env.BM_WEBHOOK_SECRET, req.headers, rawBody);
// lanza BannerMotionError ("invalid_signature") si la firma o la hora no valen
if (alreadyProcessed(event.id)) return; // las entregas pueden repetirseX-BM-Signature (sha256= más el HMAC del cuerpo en hexadecimal, con el secreto completo) sigue llegando por ahora. En integraciones nuevas usa webhook-signature.Responde rápido; nosotros insistimos.
Esperas de 5 y 30 segundos entre intentos. La misma entrega, con el mismo webhook-id y la misma carga.
Responde con un 2xx lo antes posible y procesa el evento después.
Si respondes con un 3xx, no la seguimos: cuenta como intento fallido.
Tras 5 fallos seguidos el webhook se pausa solo y no recibe más eventos hasta que lo reactives. Las pruebas manuales no cuentan.
Las entregas que agotaron los intentos se reintentan con Reintentar, en Entregas recientes.
Es el máximo por workspace. Cada uno elige sus eventos.
Con Rotar secreto, el anterior sigue firmando 24 horas como segunda firma, para que cambies sin perder eventos.
Se aceptan http y https públicas, sin usuario ni contraseña. Se rechazan las redes privadas, locales y de metadatos.
Identifica cada evento por su id y trata los repetidos como ya procesados.
Conecta tus avisos en minutos.
Webhooks y API están en los planes Equipo, Agencia y Empresa; el plan Starter no los incluye.