Configurar
Registrar un endpoint
curl -X POST https://api.joinotify.com/webhook-endpoints \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{
"name": "produccion",
"url": "https://tu-servidor.com/webhooks/joinotify",
"events": ["messages"]
}'
{
"data": {
"id": "whe_01J8ZK...",
"name": "produccion",
"url": "https://tu-servidor.com/webhooks/joinotify",
"events": ["messages"],
"active": true,
"secret": "whsec_AbCdEf0123456789abcdefgh"
},
"warning": "Store the \"secret\" now — use it to validate the X-Joinotify-Signature-256 header."
}
A diferencia de las claves de API, el secreto de webhook sí puede consultarse después:
curl https://api.joinotify.com/webhook-endpoints/whe_01J8ZK.../secret \
-H 'Authorization: Bearer sk_live_xxx'
Requisitos de la URL
En producción rechazamos:
http://— solo HTTPS- Hosts privados:
localhost,127.*,10.*,192.168.*,169.254.*,172.16-31.*,*.internal,*.local
Es una protección contra SSRF: sin ella, un endpoint apuntando a la red interna se convertiría en un escáner.
Para desarrollo local, usa un túnel:
ngrok http 3000
Filtros
curl -X POST https://api.joinotify.com/webhook-endpoints \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{
"name": "atencion-tienda-1",
"url": "https://tu-servidor.com/webhooks/tienda-1",
"events": ["messages", "smb_message_echoes"],
"phoneNumberId": "106540352242922"
}'
| Campo | Efecto |
|---|---|
events: [] | Suscribe a todos los eventos |
events: ["messages"] | Solo lo que esté en la lista |
wabaId | Solo eventos de esa cuenta de empresa |
phoneNumberId | Solo eventos de ese número |
Los eventos de cuenta (account_update, account_alerts) y de plantilla
(message_template_status_update) no tienen número asociado. Un endpoint filtrado por
phoneNumberId no los recibe.
Si quieres todo, deja un endpoint sin filtro.
Varios endpoints
Nada impide tener más de uno — y suele ser buena idea:
# Uno para el producto
{ "name": "app", "url": "https://app.ejemplo.com/webhooks", "events": ["messages"] }
# Otro para auditoría, suscrito a todo
{ "name": "auditoria", "url": "https://logs.ejemplo.com/webhooks", "events": [] }
Cada endpoint tiene su propio secreto, su propia cola y su propio contador de fallos. Un endpoint roto no afecta a los demás.
Probar
curl -X POST https://api.joinotify.com/webhook-endpoints/whe_01J8ZK.../test \
-H 'Authorization: Bearer sk_live_xxx'
Dispara un evento sintético y lo entrega al instante, sin pasar por la cola. La respuesta trae el estado HTTP y el cuerpo que devolvió tu servidor — la forma más rápida de comprobar si tu validación de firma es correcta.
{
"data": {
"id": "whd_01J8ZM...",
"status": "delivered",
"responseStatus": 200,
"responseBody": "OK",
"durationMs": 142
}
}
El payload de prueba es un evento messages con joinotify_test: true — filtra por ese
campo si tu handler guarda todo en la base de datos.
Actualizar
curl -X PATCH https://api.joinotify.com/webhook-endpoints/whe_01J8ZK... \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{ "events": ["messages", "smb_message_echoes", "history"] }'
Los campos omitidos se quedan como están. Reactivar un endpoint desactivado
(active: true) pone a cero el contador de fallos.
Pausar y eliminar
# Pausar — conserva historial y secreto
curl -X PATCH https://api.joinotify.com/webhook-endpoints/whe_01J8ZK... \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{ "active": false }'
# Eliminar — descarta las entregas pendientes
curl -X DELETE https://api.joinotify.com/webhook-endpoints/whe_01J8ZK... \
-H 'Authorization: Bearer sk_live_xxx'
Para un mantenimiento, pausa. Eliminar es definitivo.
Rotar el secreto
curl -X POST https://api.joinotify.com/webhook-endpoints/whe_01J8ZK.../rotate-secret \
-H 'Authorization: Bearer sk_live_xxx'
El cambio es inmediato y no hay período de convivencia entre los dos secretos. Dos formas de hacerlo sin perder eventos:
Sin caída — haz que tu handler acepte dos secretos durante un tiempo: valida contra el nuevo y, si falla, contra el antiguo. Rota, confirma, retira el antiguo.
Aceptando fallos — rota y publica el nuevo secreto. Las entregas que fallen en el intervalo se reintentan automáticamente en 60 segundos.