Integração
Webhooks
Um único endpoint de produção recebe eventos operacionais da Meta e eventos internos da HookCloud.
Arquitetura de entrega
Metamessages, statuses, calls
→Webhook do partnerpor phone_number_id
HookCloudtemplate.updated, lifecycle e saúde
→Mesmo endpointassinado com HMAC
A URL salva no Partner Portal é aplicada às instâncias ativas. Quando a URL muda, a HookCloud migra os callbacks na Meta e mostra o progresso.
Referências: Webhooks Meta · Override de callback
Desafio GET da Meta
app.get('/webhooks/whatsapp', (req, res) => {
const mode = req.query['hub.mode'];
const token = req.query['hub.verify_token'];
const challenge = req.query['hub.challenge'];
if (mode === 'subscribe' && token === process.env.META_VERIFY_TOKEN) {
return res.status(200).send(challenge);
}
return res.sendStatus(403);
});Recebimento operacional da Meta
app.post('/webhooks/whatsapp', express.json({ limit: '2mb' }), async (req, res) => {
res.sendStatus(200); // confirme rápido; processe em fila
for (const entry of req.body.entry ?? []) {
for (const change of entry.changes ?? []) {
if (change.field !== 'messages') continue;
const value = change.value ?? {};
const phoneNumberId = value.metadata?.phone_number_id;
for (const message of value.messages ?? []) {
await enqueueInbound({ phoneNumberId, message });
}
for (const status of value.statuses ?? []) {
await enqueueStatus({ phoneNumberId, status });
}
}
}
});Referências: Status de mensagens · Objeto statuses
Eventos HookCloud e HMAC
A HookCloud inclui os headers abaixo. Verifique a assinatura usando o corpo bruto e rejeite timestamps antigos para reduzir replay.
| Header | Descrição |
|---|---|
X-HookCloud-Event | Tipo do evento |
X-HookCloud-Delivery | ID único da entrega |
X-HookCloud-Timestamp | Unix timestamp |
X-HookCloud-Signature | sha256=HMAC_SHA256(secret, timestamp.rawBody) |
import crypto from 'node:crypto';
export function verifyHookCloudWebhook({ rawBody, timestamp, signature, secret }) {
const age = Math.abs(Date.now() / 1000 - Number(timestamp));
if (!Number.isFinite(age) || age > 300) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
const received = String(signature || '').replace(/^sha256=/, '');
if (expected.length !== received.length) return false;
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
}Evento de template
{
"id": "UUID_DO_EVENTO",
"type": "hookcloud.meta.template.updated",
"created_at": "2026-07-21T12:00:00Z",
"partner_id": "UUID_DO_PARTNER",
"data": {
"meta_waba_id": "WABA_ID",
"template_id": "123456789",
"name": "confirmacao_agendamento",
"language": "pt_BR",
"previous_status": "PENDING",
"status": "APPROVED",
"category": "UTILITY",
"quality_status": "GREEN",
"sendable": true
}
}Use id para idempotência. Ao receber a mudança, atualize a tela e reconcilie com list-partner-meta-templates.
Troca segura da URL
- Implemente GET e POST na URL nova.
- Teste com
validate-partner-webhook-endpoint. - Aplique com
update-partner-webhook-endpoint. - Acompanhe a migração.
- Reprocesse somente as falhas.
Boas práticas de produção
- Responder HTTP 2xx em poucos segundos
- Enfileirar processamento pesado
- Deduplicar pelo ID da mensagem e ID da entrega
- Não seguir redirects no seu validador
- Registrar falhas sem conteúdo sensível
- Monitorar taxa de HTTP 4xx/5xx
- Rotacionar segredos com processo controlado
Esta página ajudou?Use o Partner Portal para suporte e compartilhe o link desta seção.
