Por Avi
Publicado em 31 de jul. de 2026
Webhooks do WhatsApp: como configurar e processar eventos recebidos com Chakra Chat
Toda integração bidirecional com WhatsApp começa com um webhook. Este guia aborda os dois tipos de webhook da Chakra, exemplos reais de payloads de eventos, verificação de assinatura HMAC, tratamento de entregas duplicadas e criação de um roteador de mensagens — tudo o que n8n, Zapier e criadores de integrações personalizadas precisam.
Toda integração bidirecional com WhatsApp — um chatbot, uma sincronização com CRM, uma automação n8n, um fluxo personalizado de notificações — começa no mesmo lugar: um webhook. A Cloud API do WhatsApp é assíncrona. Quando você envia uma mensagem, recebe a confirmação de que ela foi aceita, não de que chegou ao destino. Tudo o que acontece depois — entrega, confirmação de leitura, respostas, falhas — chega até você como um evento em um webhook que está sob seu controle.
Este guia explica como a Chakra disponibiliza esses eventos, quais são os dois tipos distintos de webhook, como são estruturados os payloads, como verificar se os eventos realmente vêm da Chakra, como lidar com as entregas duplicadas que eventualmente vão aparecer e como usar um padrão simples de roteador para processar tudo com clareza em um só lugar.
Dois tipos de webhook, não apenas um
A Chakra oferece duas maneiras separadas de receber eventos, e escolher a opção certa faz diferença na quantidade de trabalho de processamento que fica por sua conta.
| Webhook de eventos pass-through | Webhook de eventos da Chakra | |
|---|---|---|
| Formato | Payload bruto da Meta Cloud API, sem modificações | Payload simplificado e normalizado pela Chakra |
| Cobertura de eventos | Tudo o que a Meta envia — mensagens, status, envios de modelos, atualizações da conta | Um conjunto selecionado: message, status, message_echo, smb_message_echo |
| Ideal para | Equipes que já conhecem o esquema de webhook da Meta ou estão migrando integrações existentes | Equipes que querem reduzir o trabalho de processamento e manter um formato consistente, independentemente de mudanças no esquema da Meta |
| Uso típico | Fluxos no n8n/Zapier criados com base no formato de webhook documentado pela Meta | Integrações personalizadas com o backend, sincronização com CRM, lógica de roteamento mais simples |
Você pode configurar uma opção ou ambas — elas são independentes, e nada impede que você use um webhook do Chakra para sua integração principal e um webhook pass-through para uma ferramenta específica, como o n8n, que espera o formato nativo da Meta.
Como configurar o webhook pass-through
Ele envia o formato de evento da própria Meta, sem alterações, exatamente como documentado na referência de webhooks da Cloud API da Meta.
Para configurar:
- Acesse a página WhatsApp Setup no seu painel do Chakra
- Clique na aba More
- Localize o campo "Pass-through webhook URL for Meta events"
- Cole a URL do seu endpoint
- Clique em Save
É o caminho usado pela maioria das integrações com n8n e Zapier, já que essas plataformas geralmente esperam (ou são desenvolvidas para usar) o formato de webhook da própria Meta.
Como configurar o webhook de eventos do Chakra
Esse é o formato normalizado do próprio Chakra — uma estrutura consistente, independentemente do que a Meta alterar do lado dela.
Para configurar:
- Acesse WhatsApp Setup → More
- Ative a opção "Enable Chakra Webhooks"
- Informe seu endpoint no campo "Chakra webhook URL"
- Selecione os tipos de evento desejados em "Which events to send" — somente os tipos selecionados serão enviados
- Clique em Save
Seu endpoint precisa estar acessível publicamente e não exige autenticação do seu lado (a verificação é feita por assinatura HMAC — falaremos mais sobre isso abaixo).
Tipos de evento e estrutura do payload
Formato pass-through (formato nativo da Meta)
Tudo chega dentro da estrutura padrão da Meta entry → changes → value , com um campo field que indica o tipo de evento. Dois dos mais comuns:
Uma mensagem de texto recebida (field: "messages"):
{ "object": "whatsapp_business_account", "entry": [ { "id": "83784929738012", "changes": [ { "value": { "messaging_product": "whatsapp", "metadata": { "display_phone_number": "917259775805", "phone_number_id": "694794789348887" }, "contacts": [ { "profile": { "name": "John" }, "wa_id": "919911223344" } ], "messages": [ { "from": "919911223344", "id": "wamid.HBgMOTEST3AxMjU4NDMzFQIAEhgUM0VCMDM3RjlEQTNDQTBEMjQ2Q275", "timestamp": "1756322278", "type": "text", "text": { "body": "Hi, can you please help me out with an issue" } } ] }, "field": "messages" } ] } ]}Uma mensagem com imagem recebida — a mesma estrutura, mas com um type:
{ "messages": [ { "from": "919911223344", "id": "wamid.HBgMOTEST3AxMjU4NDMzFQIAEhgUM0VCMDM3RjlEQTNDQTBEMjQ2Q275", "timestamp": "1756322278", "type": "image", "image": { "mime_type": "image/jpeg", "sha256": "t8K9fuzwAoeBSOoIZWkBlI1hTflUHqSwCvGwJ+exm74=", "id": "1402363984735353" } } ]}Observe que o evento de imagem fornece apenas um ID de mídia, não o arquivo em si — você precisa buscar a mídia separadamente pelo endpoint de busca de mídia do Chakra usando esse ID.
Um evento de envio de modelo (field: "message_template_sends") — disparado quando uma mensagem de modelo enviada via API é entregue; é útil para registrar exatamente o que foi enviado e para quem, sem manter um registro de envios separado:
{ "field": "message_template_sends", "value": { "message_template_sends": [ { "id": "wamid.HBgMOTE5OTAxMjU4NDMzFQIAERgSMjNBQkQ5NDA5NTFEN0MzOTU2AA==", "timestamp": 1763821065, "to": "919911223344", "type": "template", "template": { "language": { "policy": "deterministic", "code": "en_US" }, "name": "new_year_promo_24", "components": [ { "type": "body", "parameters": [{ "type": "text", "text": "John" }] } ] } } ] }}Como este webhook transmite o formato da Meta sem modificações, os eventos de status de entrega/leitura, os eventos de erro e as atualizações no nível da conta seguem a mesma estrutura documentada na referência de webhooks da Cloud API da própria Meta — vale a pena deixar essa página nos favoritos junto com este guia.
Formato de eventos do Chakra
Cada evento chega com exatamente duas chaves no nível superior:
{ "event": "message", "payload": { /* MessagePayload or DeliveryStatusPayload, depending on event type */ }}event pode ter um de quatro valores:
message— uma mensagem recebida no seu númerostatus— uma atualização de status de entrega de uma mensagem enviada anteriormente via API (enviada, entregue, lida, falha)message_echo— uma mensagem enviada de dentro da plataforma Chakra (por exemplo, uma resposta de um agente pela caixa de entrada compartilhada); é útil se você sincroniza o histórico completo das conversas com um sistema externosmb_message_echo— uma mensagem enviada pelo lado do WhatsApp Business App; é relevante especificamente se você usa Coexistence e precisa capturar as mensagens enviadas pelo app, não apenas pela API
Essa estrutura normalizada facilita a criação de um roteador para o webhook do Chakra — você sempre direciona com base no mesmo campo event , independentemente de como esteja o esquema subjacente da Meta naquele mês.
Como verificar a autenticidade do webhook com HMAC
Como o endpoint do seu webhook é público e não exige autenticação de entrada, é importante verificar se um evento realmente veio do Chakra (e não foi adulterado) antes de agir com base nele.
Configuração:
- Acesse Admin → Team → Secrets e defina um segredo HMAC
- Depois de definido, cada chamada do webhook inclui um cabeçalho
X-Chakra-Signature-256
Lógica de verificação: gere um hash HMAC-SHA256 da string bruta do corpo da requisição usando seu segredo como chave e compare-o com o valor do cabeçalho.
const crypto = require("crypto");function verifyChakraSignature(rawBody, signatureHeader, secret) { const expectedHash = crypto .createHmac("sha256", secret) .update(rawBody) // must be the raw string, not a parsed object .digest("hex"); // timing-safe comparison return crypto.timingSafeEqual( Buffer.from(expectedHash, "utf8"), Buffer.from(signatureHeader, "utf8") );}app.post("/webhooks/chakra", express.raw({ type: "*/*" }), (req, res) => { const rawBody = req.body.toString("utf8"); const signature = req.headers["x-chakra-signature-256"]; if (!verifyChakraSignature(rawBody, signature, process.env.CHAKRA_HMAC_SECRET)) { return res.status(401).send("Invalid signature"); } const event = JSON.parse(rawBody); handleEvent(event); res.sendStatus(200);});O detalhe que costuma confundir: você precisa gerar o hash da string bruta do corpo, não de uma versão que foi convertida para JSON e serializada novamente — a maioria dos frameworks analisa o corpo automaticamente antes de executar seu handler, então normalmente você precisa de um middleware de corpo bruto (como o express.rawmostrado acima) especificamente para essa rota. Observe também que o valor do cabeçalho não tem o prefixo sha256= , ao contrário das convenções HMAC de alguns outros provedores — não remova um prefixo que não está lá.
Como lidar com eventos duplicados
A entrega de webhooks do WhatsApp é, na prática, de pelo menos uma vez, não exatamente uma vez — novas tentativas de rede, indisponibilidade temporária do endpoint ou tentativas do lado da Meta podem fazer com que o mesmo evento chegue mais de uma vez. Implementar a deduplicação desde o início evita o processamento duplicado de uma mensagem (enviar duas respostas, contar duas vezes um status de entrega ou disparar um fluxo duas vezes).
A chave confiável para deduplicação é o ID da mensagem (wamid... para mensagens, ou o campo de ID correspondente para eventos de status) — armazene os IDs processados recentemente e confira se eles já foram processados antes de agir:
const processedIds = new Map(); // swap for Redis/DB in productionconst DEDUP_TTL_MS = 10 * 60 * 1000; // 10 minutes is generally enoughfunction isDuplicate(messageId) { const seenAt = processedIds.get(messageId); if (seenAt && Date.now() - seenAt < DEDUP_TTL_MS) return true; processedIds.set(messageId, Date.now()); return false;}function handleEvent(event) { const messageId = event.payload?.id || event.payload?.messages?.[0]?.id; if (messageId && isDuplicate(messageId)) { return; // already processed, skip silently } routeEvent(event);}Em produção, use Redis ou seu banco de dados com um TTL, em vez de um Map em memória — um Map de processo único não sobrevive a uma reinicialização nem funciona em várias instâncias do servidor.
Como criar um roteador simples de mensagens
Depois que os eventos forem verificados e deduplicados, o próximo passo prático é encaminhá-los ao handler certo com base no tipo. Veja um roteador básico para o formato de eventos do Chakra:
function routeEvent(event) { switch (event.event) { case "message": return handleInboundMessage(event.payload); case "status": return handleStatusUpdate(event.payload); case "message_echo": return handleAgentReply(event.payload); case "smb_message_echo": return handleAppSentMessage(event.payload); default: console.warn("Unhandled event type:", event.event); }}function handleInboundMessage(payload) { const messageType = payload.type; // "text", "image", "interactive", etc. switch (messageType) { case "text": return processTextMessage(payload); case "image": case "document": return processMediaMessage(payload); // fetch media by ID separately case "interactive": return processButtonOrListReply(payload); default: console.log("Unrecognized message type:", messageType); }}function handleStatusUpdate(payload) { // payload.status: "sent" | "delivered" | "read" | "failed" updateMessageRecordInDb(payload.id, payload.status, payload.errorCode);}Esse mesmo padrão de ramificação também funciona com o webhook pass-through — você usaria changes[].field (messages, message_template_sends, message_status ) em vez de event, e depois acessaria value.messages[0].type da mesma forma.
Uma observação sobre eventos de chamadas
Se você usa a Calling API do Chakra junto com o recurso de mensagens, os eventos relacionados a chamadas (iniciada, conectada, encerrada, perdida) são entregues pelo mesmo mecanismo pass-through, seguindo o esquema de webhook de eventos de chamada da própria Meta. Se o seu roteador já direciona eventos relacionados a mensagens com base em field , ampliá-lo para lidar com um campo calls segue o mesmo padrão — consulte a documentação oficial da Cloud API da Meta para saber os nomes exatos dos campos de eventos de chamada, já que esse esquema é transmitido sem modificações, em vez de ser normalizado pelo Chakra.
Como usar com o n8n
Especificamente para o n8n, a configuração mais comum usa o webhook pass-through, já que os fluxos de trabalho do n8n voltados ao WhatsApp e os modelos da comunidade geralmente são desenvolvidos para usar o formato nativo da Meta:
- Configure um nó de gatilho Webhook no n8n e copie a URL de teste/produção
- Cole essa URL no campo "Pass-through webhook URL for Meta events" em WhatsApp Setup → More
- Adicione um nó de filtro/condição (if) no início do fluxo de trabalho para descartar eventos que não sejam mensagens (atualizações de status, confirmações de envio de modelos) se o fluxo só precisar lidar com mensagens recebidas
- A partir daí, encaminhe para um nó AI Agent, um nó de CRM ou qualquer outro recurso de que seu fluxo de trabalho precise — o payload bruto da Meta pode ser consultado em qualquer etapa
É também aí que um filtro logo no início faz diferença: sem ele, cada evento de status e entrega dispara todo o fluxo de trabalho sem necessidade, gerando custos e ruído em plataformas que cobram por execução.
Algumas dicas práticas antes de colocar isso em produção
- Responda
200rápido e processe de forma assíncrona. Confirme o recebimento do webhook imediatamente e faça qualquer processamento demorado (gravações no banco de dados, chamadas a outra API) depois de responder ou em uma tarefa em segundo plano — uma resposta lenta pode fazer com que a Meta ou o Chakra considerem a entrega malsucedida e tentem enviá-la novamente, aumentando o trabalho de lidar com duplicatas. - Registre os payloads brutos durante o desenvolvimento. O esquema da Meta tem pequenas variações entre os tipos de mensagem, e ter exemplos brutos à mão economiza tempo quando você encontra um caso extremo que seu roteador ainda não cobre.
- Mantenha seu segredo HMAC fora do controle de versão, obviamente, mas também troque-o se suspeitar que foi exposto — gerar um novo em Admin → Equipe → Segredos invalida imediatamente a assinatura antiga.
- Monitore o tempo de atividade do seu endpoint. Se um webhook estiver fora do ar quando um evento ocorrer, você perderá uma mensagem ou atualização de status, sem recuperação automática — vale a pena monitorar esse endpoint como qualquer outro serviço crítico em produção.
Juntando tudo
Webhooks são a base pouco glamorosa da qual depende todo o resto de uma integração com WhatsApp — acerte o formato do evento, a verificação e a deduplicação uma vez, e todo chatbot, sincronização de CRM ou automação que você criar sobre essa base herdará essa confiabilidade. Comece pelo tipo de webhook mais adequado às suas ferramentas (formato normalizado do Chakra para backends personalizados, repasse direto para n8n ou ferramentas nativas do esquema da Meta), verifique as assinaturas desde o primeiro dia em vez de adaptar isso depois e elimine duplicatas pelo ID da mensagem antes que seu roteador faça qualquer coisa que altere o estado.
Se você está desenvolvendo isso, consulte a documentação completa da API do Chakra , que inclui a referência completa de eventos e endpoints mencionada ao longo deste guia.