APIs & Integrações16 de fev. de 2026· 9 min leitura

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:

  1. GET — Verificação inicial (a Meta confirma que a URL é válida)
  2. 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

  1. Acesse o Meta for Developers e abra seu app
  2. No menu lateral, vá em WhatsApp > Configuração
  3. Na seção Webhook, clique em Editar
  4. Preencha a URL de callback com a URL pública do seu servidor (ex: https://seudominio.com/webhook)
  5. Preencha o Token de verificação — deve ser o mesmo valor de WEBHOOK_VERIFY_TOKEN no seu servidor
  6. Clique em Verificar e salvar
  7. 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=stdout para ver os requests em tempo real no terminal
  • O painel do ngrok em http://localhost:4040 mostra 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.

Artigos Relacionados

  • APIs & Integrações

    WhatsApp Business API: Guia Completo Para Empresas em 2026

  • APIs & Integrações

    Rate Limits WhatsApp API: Como Evitar Bloqueios e Escalar

  • Tutoriais

    Como Criar um App no Meta Developers Para WhatsApp Business API

.