Webhooks
Llamadas firmadas a un backend cuando un teléfono añade una actividad, o cuando el usuario la desactiva o la elimina.
Un endpoint de webhook es una URL a la que el servicio envía un POST. Añade endpoints en el panel. Cada uno tiene su propio secreto de firma (whs_ y 48 caracteres hexadecimales) y puede tener un identificador. Dos endpoints de un mismo equipo no pueden tener el mismo identificador. La URL debe ser http o https.
Enrutamiento por identificador
Una actividad iniciada con un webhookIdentifier envía cada evento al único endpoint con ese identificador. Una actividad iniciada sin él envía los eventos a todos los endpoints sin identificador. Un evento sin endpoint que coincida se descarta.
Así también se hacen pruebas: no hay un entorno de pruebas aparte. Deja el endpoint de producción sin identificador, e inicia las actividades de prueba con el identificador del endpoint de prueba:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
curl -X POST https://api.webliveactivity.com/api/start \ -H 'Content-Type: application/json' \ -H 'x-api-key: wla_your_key' \ -d '{ "widgetContainerId": "acme-orders", "webhookIdentifier": "staging", "brand": { "title": "Order #1234" }, "state": { "layout": "progress", "primaryText": "Order received", "progress": 0.1 } }'
Los eventos
| Evento | Se envía cuando |
|---|---|
active | Un teléfono ha abierto la actividad y está en pantalla (una vez por teléfono), o el usuario la ha vuelto a activar en la app. |
removed | El usuario ha desactivado la actividad en la app, la ha eliminado o ha borrado los datos de la app. |
POST /api/end y los reinicios no envían webhook.
La entrega
Cada evento es un POST con un cuerpo JSON:
1 2 3 4 5 6 7 8
{ "activityId": "act-8f2cqa4mx7rd", "group": "order-tracker", "event": "active", "active": true, "webhookIdentifier": "staging", "at": "2026-07-30T14:25:19.991Z" }
| Campo | Significado |
|---|---|
activityId | La actividad. Con reuse: true, es la copia propia del teléfono, no el id que devolvió start. |
group | El grupo de la actividad. "" si no se ha definido ninguno. |
event | active o removed. |
active | El indicador en pantalla de la actividad en ese momento. Actúa según event, no según este indicador. |
webhookIdentifier | El identificador de start. null si no se ha definido ninguno. |
at | Cuándo ocurrió el evento, como hora ISO. |
Y tres encabezados:
| Encabezado | Significado |
|---|---|
X-WebActivities-Event | El nombre del evento: active o removed. |
X-WebActivities-Timestamp | Segundos de época Unix en que se envió este intento. Cada reintento recibe uno nuevo. Rechaza las marcas de tiempo antiguas. |
X-WebActivities-Signature | La firma HMAC que se describe abajo. |
Verificar la firma
La firma es un HMAC-SHA256 de la marca de tiempo y el cuerpo sin procesar de la solicitud, con el secreto del endpoint como clave. La marca de tiempo va dentro del payload firmado, así que una entrega capturada no se puede reenviar después con una marca de tiempo nueva.
1 2 3 4 5 6 7 8 9 10 11 12
const { createHmac, timingSafeEqual } = require('node:crypto'); // rawBody is the request body EXACTLY as received, before any JSON parsing. const verify = (secret, headers, rawBody) => { const timestamp = headers['x-webactivities-timestamp']; const expected = 'sha256=' + createHmac('sha256', secret) .update(timestamp + '.' + rawBody) .digest('hex'); const presented = Buffer.from(headers['x-webactivities-signature']); return expected.length === presented.length && timingSafeEqual(Buffer.from(expected), presented); };
Compara en tiempo constante (timingSafeEqual) para que una firma incorrecta no revele nada de la correcta, y compara la marca de tiempo con un reloj local para bloquear reenvíos.
Reintentos
- El endpoint tiene 8 segundos para responder con un 2xx. Una redirección cuenta como fallo.
- Una entrega fallida se intenta 5 veces en total, con esperas de 1, 2, 4 y 8 minutos entre intentos.
- Las URL que apuntan a direcciones privadas o internas se rechazan.
- El registro de entregas del panel muestra cada entrega durante 90 días y puede volver a enviarla.
Responder rápido
Guarda el evento y responde 200 primero, y después actúa. El trabajo lento dentro de la solicitud arriesga el tiempo de espera de 8 segundos y un reintento que llega duplicado. Los reintentos pueden llegar desordenados, así que usa el campo at como la hora real del evento.









