WAZGO PUBLIC API · V1
Webhooks
Eventos suportados nesta versão:
| Evento | Quando | Dados públicos |
|---|---|---|
| campaign.sent | Uma entrega de campanha foi marcada como enviada. | deliveryId, campaignId, groupId, sentAt |
| campaign.failed | Uma entrega de campanha falhou. | deliveryId, campaignId, groupId, failedAt |
| group.synced | Um snapshot persistido de grupos foi sincronizado. | syncId, groupsFound, created, updated |
| webhook.test | Evento sintético enviado pelo painel. | test: true |
Envelope
{
"id": "evt_exemplo",
"type": "campaign.sent",
"version": "1",
"createdAt": "2026-10-02T12:00:00.000Z",
"workspaceId": "workspace_exemplo",
"data": {
"deliveryId": "delivery_exemplo",
"campaignId": "campaign_exemplo",
"groupId": "group_exemplo",
"sentAt": "2026-10-02T12:00:00.000Z"
}
}Validar assinatura
O header X-Wazbot-Signature contém v1= seguido do HMAC-SHA256 hexadecimal minúsculo de timestamp + "." + rawBody. O timestamp em Unix seconds está em X-Wazbot-Timestamp. Valide os bytes originais, compare em tempo constante e rejeite timestamps com mais de cinco minutos.
import crypto from "node:crypto";
import express from "express";
const app = express();
app.post("/webhooks/wazgo", express.raw({ type: "application/json" }), (req, res) => {
const secret = process.env.WAZGO_WEBHOOK_SECRET;
const timestamp = req.header("X-Wazbot-Timestamp");
const signature = req.header("X-Wazbot-Signature")?.replace(/^v1=/, "");
if (!secret || !timestamp || !signature || !Buffer.isBuffer(req.body)) return res.sendStatus(400);
const sentAt = Number(timestamp);
if (!Number.isInteger(sentAt) || Math.abs(Date.now() / 1000 - sentAt) > 300) return res.sendStatus(400);
const expected = crypto.createHmac("sha256", secret).update(timestamp + ".").update(req.body).digest();
const received = Buffer.from(signature, "hex");
if (received.length !== expected.length || !crypto.timingSafeEqual(received, expected)) return res.sendStatus(401);
const event = JSON.parse(req.body.toString("utf8"));
// Deduplicate event.id and enqueue the work before acknowledging.
return res.sendStatus(204);
});Entrega, tentativas e replay
- Qualquer resposta HTTP 2xx confirma a entrega.
- A semântica é at-least-once: um evento pode chegar mais de uma vez; deduplique pelo campo
id. - Falhas temporárias, timeout e HTTP 408/425/429/5xx podem ser repetidos. As tentativas são limitadas; atrasos exatos não são um contrato público.
- Não há garantia de ordem global. Reconcile o estado atual pela API quando necessário.
- O painel permite reenviar entregas elegíveis; o
eventIdoriginal permanece e a requisição recebe novo timestamp e assinatura.
Use HTTPS. Configure o servidor para capturar o body bruto antes de qualquer parser JSON. Faça a deduplicação e enfileire seu trabalho antes de responder 2xx.