Tus endpoints reciben eventos firmados cuando pasa algo en tus cuentas conectadas. Reintentos con backoff si tu endpoint falla.
/hookssecret una sola vez (para verificar la firma)./hooks · /hooks/:id/hooks/:id/hooks/:id/testwebhook.test)./hooks/:id/deliveriesCada entrega lleva X-SocialGate-Signature: sha256=<hmac> (HMAC del body crudo, tal cual llega, con tu secret), más X-SocialGate-Event y X-SocialGate-Delivery. No re-serialices el JSON antes de comparar: el orden de las claves cambia y la firma deja de coincidir.
Un webhook creado con una API key acotada a ciertos perfiles (scope: 'profiles') solo recibe los eventos de esos perfiles. Creado con una key sin restricción, recibe los de todo el owner.
Esto importa si eres una agencia o un integrador: si le das a alguien una key limitada al perfil de su cliente, el webhook que registre no verá los leads ni los mensajes del resto de tus perfiles. Esa key tampoco puede listar, editar ni consultar el historial de entregas de webhooks que cubran perfiles fuera de su alcance — responden 403.
Límites: 20 webhooks por cuenta y un ping de prueba cada 30 segundos por webhook.
Todos los eventos comparten el mismo sobre y llevan lo específico anidado en data — no en la raíz.
{
"event": "message.received",
"ownerId": "…",
"profileId": "prof_123",
"data": { "accountId": "acc_456", "from": "34600000000", "text": "hola", "msgId": "…", "platform": "whatsapp" }
}post.scheduledpost.publishedpost.partialpost.failedpost.platform.failedaccount.connectedaccount.needs_reconnectionaccount.disconnectedlead.receivedmessage.receivedautomation.triggeredwebhook.testaccount.disconnected lo dispara DELETE /accounts/:id y DELETE /adaccounts/:id — es decir, cuando la desconexión la inicia alguien desde Zoosial. Si el usuario revoca el permiso desde Facebook, eso llega como account.needs_reconnection, no como disconnected.Para probar tu receptor sin esperar a que pase nada real: POST /hooks/:id/test encola un webhook.test, y el simulador de leads emite un lead.received con datos realistas.
const crypto = require("crypto"); app.post("/hooks/sc", express.raw({ type: "application/json" }), (req, res) => { const expected = "sha256=" + crypto.createHmac("sha256", WEBHOOK_SECRET).update(req.body).digest("hex"); if (req.headers["x-socialgate-signature"] !== expected) return res.sendStatus(401); // req.headers["x-socialgate-event"] === "message.received", etc. res.json({ ok: true }); });