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

javascript
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

javascript
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.

HeaderDescrição
X-HookCloud-EventTipo do evento
X-HookCloud-DeliveryID único da entrega
X-HookCloud-TimestampUnix timestamp
X-HookCloud-Signaturesha256=HMAC_SHA256(secret, timestamp.rawBody)
Node.jsjavascript
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

json
{
  "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

  1. Implemente GET e POST na URL nova.
  2. Teste com validate-partner-webhook-endpoint.
  3. Aplique com update-partner-webhook-endpoint.
  4. Acompanhe a migração.
  5. 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.