Como Configurar Webhooks na WhatsApp Business API: Guia Técnico Completo
Guia prático para desenvolvedores configurarem webhooks da WhatsApp Business API com exemplos de código em Node.js e Python, validação de segurança e troubleshooting.
Se você está começando a integrar a WhatsApp Business API, configurar webhooks é o primeiro desafio real. Sem webhooks, sua aplicação não recebe mensagens, notificações de entrega ou atualizações de status — ela fica cega.
Neste guia, vou mostrar passo a passo como configurar, validar e processar webhooks da Meta, com código funcional e soluções para os problemas mais comuns.
O Que São Webhooks e Por Que Você Precisa Deles
Webhooks são URLs de callback que a Meta chama automaticamente quando algo acontece na sua conta WhatsApp. Em vez de ficar fazendo polling (verificando repetidamente se há novidades), seu servidor recebe os eventos em tempo real.
Eventos que você recebe via webhook:
- Mensagens recebidas — texto, imagem, áudio, documento, localização
- Status de entrega — enviado, entregue, lido
- Erros de entrega — mensagem não entregue, número inválido
- Mudanças na conta — alterações de template, atualizações de perfil
Sem webhooks configurados, sua aplicação simplesmente não funciona como um canal bidirecional de comunicação.
Anatomia de um Webhook Request da Meta
Antes de implementar, você precisa entender a estrutura que a Meta envia. Todo request é um POST com um payload JSON.
Estrutura Base do Payload
{
"object": "whatsapp_business_account",
"entry": [
{
"id": "WHATSAPP_BUSINESS_ACCOUNT_ID",
"changes": [
{
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "5511999999999",
"phone_number_id": "PHONE_NUMBER_ID"
},
"contacts": [
{
"profile": { "name": "João Silva" },
"wa_id": "5511988888888"
}
],
"messages": [
{
"from": "5511988888888",
"id": "wamid.xxxxx",
"timestamp": "1708099200",
"text": { "body": "Olá, quero saber sobre o produto X" },
"type": "text"
}
]
},
"field": "messages"
}
]
}
]
}
Evento de Status de Entrega
{
"value": {
"messaging_product": "whatsapp",
"metadata": {
"display_phone_number": "5511999999999",
"phone_number_id": "PHONE_NUMBER_ID"
},
"statuses": [
{
"id": "wamid.xxxxx",
"status": "delivered",
"timestamp": "1708099260",
"recipient_id": "5511988888888"
}
]
}
}
Os status possíveis são: sent, delivered, read e failed.
Passo 1: Criar o Endpoint de Webhook
Seu servidor precisa responder a dois tipos de request:
- GET — Verificação inicial (a Meta confirma que a URL é válida)
- POST — Recebimento de eventos em tempo real
Node.js com Express
const express = require('express');
const app = express();
app.use(express.json());
const VERIFY_TOKEN = process.env.WEBHOOK_VERIFY_TOKEN;
// Verificação do webhook (GET)
app.get('/webhook', (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 === VERIFY_TOKEN) {
console.log('Webhook verificado com sucesso');
return res.status(200).send(challenge);
}
return res.sendStatus(403);
});
// Recebimento de eventos (POST)
app.post('/webhook', (req, res) => {
// IMPORTANTE: responda 200 imediatamente
res.sendStatus(200);
const body = req.body;
if (body.object !== 'whatsapp_business_account') return;
body.entry?.forEach(entry => {
entry.changes?.forEach(change => {
const value = change.value;
// Processar mensagens recebidas
if (value.messages) {
value.messages.forEach(message => {
console.log(`Mensagem de ${message.from}: ${message.text?.body}`);
// Aqui você processa a mensagem (salvar no banco, responder, etc.)
});
}
// Processar status de entrega
if (value.statuses) {
value.statuses.forEach(status => {
console.log(`Status: ${status.status} para ${status.recipient_id}`);
});
}
});
});
});
app.listen(3000, () => console.log('Servidor rodando na porta 3000'));
Python com Flask
from flask import Flask, request, jsonify
import os
app = Flask(__name__)
VERIFY_TOKEN = os.environ.get('WEBHOOK_VERIFY_TOKEN')
@app.route('/webhook', methods=['GET'])
def verify_webhook():
mode = request.args.get('hub.mode')
token = request.args.get('hub.verify_token')
challenge = request.args.get('hub.challenge')
if mode == 'subscribe' and token == VERIFY_TOKEN:
print('Webhook verificado com sucesso')
return challenge, 200
return 'Forbidden', 403
@app.route('/webhook', methods=['POST'])
def receive_webhook():
body = request.get_json()
if body.get('object') != 'whatsapp_business_account':
return 'OK', 200
for entry in body.get('entry', []):
for change in entry.get('changes', []):
value = change.get('value', {})
# Processar mensagens recebidas
for message in value.get('messages', []):
sender = message.get('from')
text = message.get('text', {}).get('body', '')
print(f'Mensagem de {sender}: {text}')
# Processar status de entrega
for status in value.get('statuses', []):
print(f"Status: {status['status']} para {status['recipient_id']}")
return 'OK', 200
if __name__ == '__main__':
app.run(port=3000)
Passo 2: Configurar no Meta Business Manager
- Acesse o Meta for Developers e abra seu app
- No menu lateral, vá em WhatsApp > Configuração
- Na seção Webhook, clique em Editar
- Preencha a URL de callback com a URL pública do seu servidor (ex:
https://seudominio.com/webhook) - Preencha o Token de verificação — deve ser o mesmo valor de
WEBHOOK_VERIFY_TOKENno seu servidor - Clique em Verificar e salvar
- Após a verificação, inscreva-se nos campos que deseja receber: selecione pelo menos
messages
A Meta fará um request GET para sua URL com os parâmetros hub.mode, hub.verify_token e hub.challenge. Se seu servidor responder corretamente, o webhook é ativado.
Passo 3: Validação de Signature (Segurança)
A Meta assina cada request com um hash HMAC-SHA256 usando o App Secret. Validar essa assinatura garante que o request realmente veio da Meta.
Node.js
const crypto = require('crypto');
function validateSignature(req, buf) {
const signature = req.headers['x-hub-signature-256'];
if (!signature) return false;
const appSecret = process.env.META_APP_SECRET;
const expectedSignature = 'sha256=' +
crypto.createHmac('sha256', appSecret).update(buf).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Middleware para validar antes de processar
app.use('/webhook', express.json({
verify: (req, res, buf) => {
if (!validateSignature(req, buf)) {
throw new Error('Assinatura inválida');
}
}
}));
Python
import hmac
import hashlib
def validate_signature(request):
signature = request.headers.get('X-Hub-Signature-256', '')
app_secret = os.environ.get('META_APP_SECRET')
expected = 'sha256=' + hmac.new(
app_secret.encode(),
request.data,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
@app.before_request
def check_signature():
if request.path == '/webhook' and request.method == 'POST':
if not validate_signature(request):
return 'Invalid signature', 403
Nunca pule a validação de signature em produção. Sem ela, qualquer pessoa pode enviar requests falsos para seu endpoint.
Passo 4: Testando Localmente com ngrok
Durante o desenvolvimento, você precisa de uma URL pública. O ngrok cria um túnel seguro para seu localhost.
# Instale o ngrok
npm install -g ngrok
# Inicie seu servidor local
node server.js
# Em outro terminal, crie o túnel
ngrok http 3000
O ngrok vai gerar uma URL como https://abc123.ngrok-free.app. Use essa URL no Meta Business Manager como URL de callback.
Dicas importantes:
- A URL muda toda vez que você reinicia o ngrok (versão gratuita)
- Use
ngrok http 3000 --log=stdoutpara ver os requests em tempo real no terminal - O painel do ngrok em
http://localhost:4040mostra todos os requests recebidos — muito útil para debug
Estrutura de Eventos por Tipo
Mensagem de Texto
{
"type": "text",
"text": { "body": "Conteúdo da mensagem" }
}
Mensagem de Imagem
{
"type": "image",
"image": {
"id": "MEDIA_ID",
"mime_type": "image/jpeg",
"sha256": "HASH",
"caption": "Legenda opcional"
}
}
Mensagem de Áudio
{
"type": "audio",
"audio": {
"id": "MEDIA_ID",
"mime_type": "audio/ogg"
}
}
Mensagem de Documento
{
"type": "document",
"document": {
"id": "MEDIA_ID",
"mime_type": "application/pdf",
"filename": "proposta.pdf"
}
}
Mensagem de Localização
{
"type": "location",
"location": {
"latitude": -23.5505,
"longitude": -46.6333,
"name": "São Paulo",
"address": "Av. Paulista, 1000"
}
}
Para baixar mídia (imagem, áudio, documento), use o endpoint GET /{media_id} com seu token de acesso para obter a URL de download.
Troubleshooting: Problemas Comuns
Webhook não recebe eventos
Causa 1: SSL inválido ou autoassinado A Meta exige certificado SSL válido. Certificados autoassinados não funcionam. Use Let's Encrypt (gratuito) ou um serviço como Cloudflare.
Causa 2: Servidor não responde 200 em tempo
Seu endpoint deve retornar 200 OK em menos de 20 segundos. Se demorar mais, a Meta considera como falha.
Causa 3: Não inscrito nos campos corretos
Após configurar o webhook, você precisa se inscrever nos campos. Vá em WhatsApp > Configuração > Webhook e marque os campos desejados (ex: messages).
Meta retenta o webhook e duplica mensagens
A Meta faz retry automático quando não recebe 200. Para evitar processamento duplicado:
const processedMessages = new Set();
app.post('/webhook', (req, res) => {
res.sendStatus(200); // Responda IMEDIATAMENTE
req.body.entry?.forEach(entry => {
entry.changes?.forEach(change => {
change.value.messages?.forEach(message => {
// Idempotência: ignore mensagens já processadas
if (processedMessages.has(message.id)) return;
processedMessages.add(message.id);
// Processe a mensagem de forma assíncrona
processMessageAsync(message).catch(console.error);
});
});
});
});
Em produção, substitua o Set por Redis ou banco de dados para persistir entre restarts.
Erro 400 na verificação
Verifique se o WEBHOOK_VERIFY_TOKEN no seu servidor é exatamente igual ao que você configurou no Meta Business Manager. Espaços extras ou caracteres invisíveis são uma causa comum.
Boas Práticas de Implementação
1. Responda 200 Antes de Processar
A regra mais importante: retorne 200 imediatamente e processe o evento de forma assíncrona. Se você processar antes de responder e demorar mais de 20 segundos, a Meta vai considerar como falha e retentar.
app.post('/webhook', (req, res) => {
res.sendStatus(200); // Primeiro: responder
// Depois: processar em background
processWebhookEvent(req.body)
.catch(err => console.error('Erro ao processar webhook:', err));
});
2. Implemente Idempotência
A Meta pode enviar o mesmo evento mais de uma vez (retries). Use o message.id ou status.id como chave de idempotência para evitar processar duplicatas.
3. Use Filas para Processamento
Em aplicações de alto volume, coloque os eventos em uma fila (Redis Queue, Bull, SQS) em vez de processar inline:
const Queue = require('bull');
const webhookQueue = new Queue('webhook-events', process.env.REDIS_URL);
app.post('/webhook', (req, res) => {
res.sendStatus(200);
webhookQueue.add(req.body);
});
webhookQueue.process(async (job) => {
await processWebhookEvent(job.data);
});
4. Monitore e Registre
Registre todos os eventos recebidos para debug e auditoria. Em caso de problema, os logs são sua primeira fonte de diagnóstico.
5. Trate Cada Tipo de Mensagem
Não assuma que toda mensagem é texto. Sempre verifique o campo type antes de acessar os dados:
function handleMessage(message) {
switch (message.type) {
case 'text':
return handleText(message.text.body);
case 'image':
return handleMedia(message.image);
case 'audio':
return handleMedia(message.audio);
case 'document':
return handleMedia(message.document);
case 'location':
return handleLocation(message.location);
default:
console.log(`Tipo não suportado: ${message.type}`);
}
}
Conclusão
Webhooks são a base de qualquer integração funcional com a WhatsApp Business API. Com o endpoint configurado corretamente, a validação de segurança implementada e o processamento assíncrono, sua aplicação estará preparada para receber e processar mensagens em tempo real.
Os pontos mais importantes para lembrar:
- Responda 200 imediatamente — processe depois
- Valide a signature — nunca confie em requests sem verificação
- Implemente idempotência — a Meta faz retries
- Use HTTPS válido — certificados autoassinados não funcionam
- Teste com ngrok — antes de fazer deploy, valide localmente
Se você está começando agora com a WhatsApp Business API, confira também meu guia sobre como criar um App no Meta for Developers para configurar todo o ambiente necessário antes de implementar webhooks.