WaflyTutoriais › Webhook de WhatsApp

Webhook de WhatsApp: eventos, payload completo e como receber na sua aplicação

Atualizado em julho de 2026

um webhook de WhatsApp é uma URL sua que recebe eventos por POST. São 4 eventos de instância: mensagem recebida, status de entrega, conectada e desconectada. Configure a URL via API, responda 200 rápido e processe de forma assíncrona. Abaixo, o payload completo campo a campo, os 5 tipos de callback e um receiver Node.js pronto.

Enviar mensagem por API é a metade fácil. O que transforma um disparador em bot de verdade é o caminho contrário: a mensagem do cliente chegar na sua aplicação, em tempo real. Isso é o webhook: a API chama uma URL sua a cada evento da instância.

Os 4 webhooks da instância

EventoEndpoint de configuraçãoQuando dispara
Mensagem recebidaupdate-webhook-receivedA instância recebe qualquer mensagem
Entrega confirmadaupdate-webhook-deliveryMensagem enviada é confirmada como entregue
Instância conectadaupdate-webhook-connectedA sessão conecta ao WhatsApp
Instância desconectadaupdate-webhook-disconnectedA sessão cai. Funciona como alerta de monitoramento de graça

Passo 1: configurar a URL

Cada webhook é um PUT com a URL de destino no corpo (string vazia "" remove):

curl -X PUT "https://wafly.com.br/api-bridge-whats/instances/SUA_INSTANCIA/token/SEU_TOKEN/update-webhook-received" \
  -H "Client-Token: SEU_CLIENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "value": "https://meuservidor.com/webhook/whatsapp" }'

O payload de mensagem recebida, campo a campo

Toda mensagem recebida chega como POST com Content-Type: application/json. Este é o corpo real de uma mensagem de texto em conversa privada:

{
  "type": "ReceivedCallback",
  "instanceId": "minha-instancia",
  "connectedPhone": "5511999999999",
  "phone": "5511988887777",
  "senderName": "Maria Souza",
  "senderPhoto": "https://pps.whatsapp.net/...",
  "senderLid": "5511988887777@s.whatsapp.net",
  "chatName": "Maria Souza",
  "messageId": "3EB0C767D26B8F1A2C31",
  "momment": 1785312000000,
  "status": "RECEIVED",
  "fromMe": false,
  "isGroup": false,
  "isNewsletter": false,
  "isEdit": false,
  "isStatusReply": false,
  "waitingMessage": false,
  "broadcast": false,
  "text": { "message": "Oi, ainda tem em estoque?" }
}
CampoTipoO que contém
typestringTipo do evento. Em mensagem recebida é sempre ReceivedCallback
instanceIdstringIdentificador da instância que recebeu — útil quando você aponta várias para a mesma URL
connectedPhonestringO seu número, o que está conectado na instância
phonestringA conversa de origem: o número do contato, ou o ID do grupo quando isGroup é true
participantPhonestringSó em grupo: quem, dentro do grupo, mandou a mensagem
senderNamestringNome de exibição (push name) do remetente
senderPhotostringURL da foto de perfil, quando disponível
senderLidstringJID interno do remetente
chatNamestringNome da conversa: do contato, ou do grupo
messageIdstringID único da mensagem. Use como chave de idempotência
mommentnumberTimestamp em epoch de milissegundos (atenção: o campo tem dois "m" mesmo)
statusstringRECEIVED para mensagem de terceiro, SENT quando fromMe é true
fromMebooleanTrue quando a mensagem foi enviada pelo próprio número conectado. Filtre isso ou seu bot responde a si mesmo
isGroupbooleanTrue quando a origem é um grupo
isNewsletterbooleanTrue quando a origem é um canal
isEditbooleanTrue quando é a edição de uma mensagem já enviada
isStatusReplybooleanTrue quando é resposta a um status
broadcastbooleanTrue em mensagem de lista de transmissão
text.messagestringO conteúdo, em mensagem de texto

Como distinguir texto, mídia e grupo

O envelope acima é o mesmo em todos os casos; o que muda é a chave de conteúdo. Em texto vem text.message. Em mídia (imagem, áudio, vídeo, documento, sticker), localização, contato, enquete e reação vem uma chave própria de cada tipo, no lugar de text. O caminho mais rápido de descobrir a sua é logar o payload inteiro na primeira mensagem de teste.

Para grupo, a diferença prática é que phone passa a ser o ID do grupo e aparece participantPhone com quem falou. Se você responder usando phone, a resposta cai no grupo; se usar participantPhone, cai no privado da pessoa — e aí vale lembrar que mandar privado para quem nunca te escreveu é o caminho mais curto para o erro 463 e para o banimento do número.

Os outros tipos de callback

O campo type distingue os eventos que chegam na sua URL. Faça o roteamento por ele:

typeQuando chegaCampos próprios
ReceivedCallbackMensagem recebida (ou enviada, com fromMe: true)ver tabela acima
MessageStatusCallbackMudança de status de mensagens que você envioustatus, ids (array), phone, participant em grupo
DeliveryCallbackConfirmação de entregaphone, momment
ConnectedCallbackA instância conectouconnected: true, instanceId
DisconnectedCallbackA sessão caiudisconnected: true, error, reason

O DisconnectedCallback é o mais subestimado dos cinco. Ele traz reason, que diz por que a sessão caiu — e é a diferença entre descobrir a queda pelo seu monitoramento ou pelo cliente reclamando que ninguém respondeu. Trate como alerta, não como log.

Vale a ressalva honesta: numa API não oficial o webhook depende da sessão estar de pé. Se a sessão cai, os eventos param até reconectar — não há fila persistida nem garantia de reentrega como na Cloud API oficial da Meta. Por isso o alerta de desconexão importa tanto aqui.

Passo 2: receber (exemplo em Node.js)

import express from "express";

const app = express();
app.use(express.json());

const processados = new Set(); // troque por Redis/banco em produção

app.post("/webhook/whatsapp", (req, res) => {
  res.sendStatus(200); // responda JÁ; processe depois

  const evt = req.body;

  // 1. roteie pelo tipo do evento
  if (evt.type === "DisconnectedCallback") {
    return alertar(`Sessão caiu: ${evt.reason}`); // seu alerta aqui
  }
  if (evt.type !== "ReceivedCallback") return;

  // 2. ignore o que você mesmo enviou, ou o bot responde a si mesmo
  if (evt.fromMe) return;

  // 3. idempotência: o mesmo evento pode chegar duas vezes
  if (processados.has(evt.messageId)) return;
  processados.add(evt.messageId);

  // 4. só texto, neste exemplo
  const texto = evt.text?.message;
  if (!texto) return;

  const de = evt.isGroup ? evt.participantPhone : evt.phone;
  console.log(`[${new Date(evt.momment).toISOString()}] ${de}: ${texto}`);

  // responder: use evt.phone (cai no grupo se for grupo)
});

app.listen(3000);

Na primeira mensagem de teste, logue o payload inteiro e inspecione, que é o jeito mais rápido de ver todos os campos disponíveis para o seu caso (texto, mídia, remetente, grupo). Em desenvolvimento local, exponha a porta com um túnel (ngrok, cloudflared) para o webhook alcançar sua máquina.

Boas práticas que evitam dor

Sem servidor? Use n8n

O Webhook node do n8n gera a URL pública por você. Cadastre-a no update-webhook-received e cada mensagem vira uma execução do workflow. O caminho completo está no tutorial WhatsApp no n8n.

Teste com uma instância real

3 dias grátis, sem cartão. Configure o webhook e veja o payload chegando em minutos.

Criar instância grátis

Perguntas frequentes

Qual é o payload do webhook de mensagem recebida?

JSON com type: "ReceivedCallback" e os campos instanceId, connectedPhone, phone, participantPhone (só em grupo), senderName, chatName, messageId, momment (epoch ms), status, mais os booleanos fromMe, isGroup, isNewsletter, isEdit, isStatusReply e broadcast. Em texto, o conteúdo vem em text.message. Tabela completa acima.

Como evitar que o bot responda às próprias mensagens?

Descarte todo evento com fromMe: true. O que você envia também gera ReceivedCallback, com status: "SENT". Sem esse filtro o bot entra em laço.

O que é um webhook de WhatsApp?

Uma URL sua que a API chama a cada evento da instância (mensagem recebida, entrega, conexão, desconexão). É o que torna o bot bidirecional.

Funciona com n8n?

Sim. O Webhook node gera a URL; cadastre-a como webhook de recebidas e cada mensagem dispara o workflow.

Preciso responder rápido?

Sim: 200 imediato, processamento assíncrono depois. Lentidão gera retries e eventos duplicados.

Como removo um webhook?

Faça o mesmo PUT com "value": "" (string vazia).

Continue

Integrar WhatsApp ao CRM com webhookIdempotência, receiver, contato, negócio e resposta pela API. Transcrever áudio no webhookEntregue texto de voz direto para IA, n8n ou código próprio. Enviar mensagem por APIcurl, Node.js e Python (a outra metade do bot). WhatsApp no n8nWebhook + envio sem escrever código. Documentação completaTodos os endpoints, parâmetros e exemplos.

Endpoints verificados em julho/2026. Encontrou algo impreciso? Fale com a gente que corrigimos.