Todos os artigos
Eventos de mensagens móveis passando pela verificação de webhook da Meta e por um túnel seguro até uma aplicação no localhost.
WhatsApp Cloud APIMeta webhookslocalhostwebhook security

Testar webhooks da WhatsApp Cloud API no localhost

Para testar um webhook da API do WhatsApp Cloud no host local, exponha seu endpoint local por meio de HTTPS, implemente o desafio de verificação GET do Meta e, em seguida, verifique cada solicitação POST X-Hub-Signature-256 em relação ao corpo bruto. Registre o URL do túnel em seu Meta app, assine a conta comercial do WhatsApp em messages e envie uma mensagem de teste para receber uma carga útil real sem implantação.

Os webhooks do WhatsApp usam dois fluxos de verificação diferentes

A distinção mais importante é que a configuração e a entrega do webhook são autenticadas de maneira diferente. Durante a configuração, Meta envia uma solicitação GET contendo hub.mode, hub.verify_token e hub.challenge. Seu endpoint compara o token de verificação e retorna o desafio como texto simples. Posteriormente, as entregas de eventos são solicitações POST; eles devem ser autenticados validando a assinatura HMAC criada com seu Meta App Secret.

Um token de verificação é uma string aleatória que você escolhe; não é o token de acesso do WhatsApp e nem o segredo do aplicativo. Retornar o desafio prova o controle do endpoint de retorno de chamada. Ele não autentica solicitações POST futuras. guia oficial do webhook do WhatsApp do Meta abrange configuração de retorno de chamada, assinaturas e campos de webhook.

Criar um endpoint do Next.js App Router

A rota abaixo lida com ambas as fases. Ler dados POST com request.text() preserva os bytes exatos necessários para verificação de assinatura.

// app/api/webhooks/whatsapp/route.ts
import crypto from 'node:crypto';
import { NextRequest } from 'next/server';

export const runtime = 'nodejs';

export async function GET(request: NextRequest) {
  const mode = request.nextUrl.searchParams.get('hub.mode');
  const token = request.nextUrl.searchParams.get('hub.verify_token');
  const challenge = request.nextUrl.searchParams.get('hub.challenge');

  if (mode === 'subscribe' && token === process.env.META_VERIFY_TOKEN) {
    return new Response(challenge ?? '', { status: 200 });
  }
  return new Response('Forbidden', { status: 403 });
}

export async function POST(request: Request) {
  const rawBody = await request.text();
  const supplied = request.headers.get('x-hub-signature-256') ?? '';
  const expected = 'sha256=' + crypto
    .createHmac('sha256', process.env.META_APP_SECRET!)
    .update(rawBody)
    .digest('hex');

  const a = Buffer.from(supplied);
  const b = Buffer.from(expected);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return new Response('Invalid signature', { status: 401 });
  }

  const payload = JSON.parse(rawBody);
  await enqueueWhatsAppPayload(payload);
  return new Response('EVENT_RECEIVED', { status: 200 });
}

Não chame request.json() e depois reconstrua o JSON para HMAC. Espaço em branco, escape ou ordem de chave podem mudar, produzindo um resumo diferente. Se você usa Express, capture um Buffer antes de um analisador JSON global. O guia geral de assinatura de webhook explica o tratamento de corpo bruto em estruturas.

Inicie um túnel e configure o retorno de chamada

  1. Execute o aplicativo Next.js localmente, normalmente com npm run dev na porta 3000.
  2. Execute npx portpreview 3000 em um terminal separado.
  3. Defina META_VERIFY_TOKEN para um valor aleatório e META_APP_SECRET para o segredo do aplicativo nas configurações do aplicativo Meta.
  4. No painel do desenvolvedor Meta, abra a página de configuração do produto WhatsApp.
  5. Defina o retorno de chamada URL para https://YOUR-TUNNEL.portpreview.dev/api/webhooks/whatsapp e insira o mesmo token de verificação.
  6. Depois que a verificação for bem-sucedida, inscreva-se no campo messages da conta do WhatsApp Business.

O túnel deve permanecer ativo durante o desafio GET e entregas POST subsequentes. Um URL copiado de uma sessão mais antiga pode resolver, mas não encaminhar mais para sua máquina, portanto, confirme o retorno de chamada exato sempre que o túnel local for alterado.

Teste o desafio GET de forma independente

Antes de usar o painel, reproduza a solicitação localmente:

curl -i \
  "http://localhost:3000/api/webhooks/whatsapp?hub.mode=subscribe&hub.verify_token=${META_VERIFY_TOKEN}&hub.challenge=123456"

Uma resposta correta é o status 200 com corpo 123456, não JSON e não "123456" com aspas. Se o token estiver errado, 403 é apropriado. Não registre parâmetros de consulta porque o token de verificação aparece lá.

Entenda a carga útil de uma mensagem antes de escrever a lógica de negócios

O WhatsApp agrupa os dados em vários níveis de profundidade. Uma notificação típica tem object: "whatsapp_business_account", um entry array, um changes array e uma mudança cujo field é messages. Dentro de value, o conteúdo recebido do usuário aparece em messages; atualizações de entrega, leitura e falha das mensagens enviadas aparecem em statuses.

for (const entry of payload.entry ?? []) {
  for (const change of entry.changes ?? []) {
    if (change.field !== 'messages') continue;
    for (const message of change.value.messages ?? []) {
      await handleInboundMessage({
        id: message.id,
        from: message.from,
        type: message.type,
        text: message.text?.body,
      });
    }
    for (const status of change.value.statuses ?? []) {
      await updateDeliveryStatus(status.id, status.status);
    }
  }
}

Não presuma que toda notificação contém uma mensagem de texto. Imagens, áudio, documentos, locais, respostas interativas, mensagens do sistema e cargas somente de status têm formatos diferentes. Mantenha um despachante codificado por message.type, valide campos opcionais e retenha tipos de eventos desconhecidos para revisão em vez de travar.

Verifique a assinatura POST corretamente

O valor X-Hub-Signature-256 usa o formato sha256=<hex digest>. Calcule HMAC-SHA256 sobre os bytes de solicitação brutos usando o Meta App Secret. O token de acesso permanente ou temporário do WhatsApp é usado para chamadas da API Graph; não é a chave HMAC. Use a comparação em tempo constante e rejeite uma assinatura ausente.

Mantenha a verificação local ativada. Qualquer pessoa que aprender um URL de túnel pode POSTAR JSON arbitrário nele. Sem verificação, um evento forjado pode acionar respostas automáticas, alterar registros de CRM ou expor o estado do cliente. Alterne o segredo do aplicativo se ele for confirmado, impresso ou compartilhado acidentalmente.

Reconhecer rapidamente e desduplicar mensagens

Retornar 200 após autenticar e enfileirar o evento de forma duradoura. Não espere enquanto baixa mídia, liga para um LLM ou atualiza vários serviços. Os provedores tentam novamente as entregas quando as confirmações falham, e a ambiguidade da rede significa que as duplicatas são normais.

Use a mensagem do WhatsApp id como chave de idempotência para mensagens de entrada e objetos de status. Coloque uma restrição exclusiva em torno dos IDs processados. As transições de status podem progredir legitimamente de enviado para entregue e para leitura, portanto, desduplicar cada transição relevante sem descartar um estado posterior.

Solucionar problemas de configuração do webhook do WhatsApp

O URL de retorno de chamada não pôde ser validado

Teste a rota GET por meio do URL público. Certifique-se de que ele aceita GET, compara o token de verificação exato e responde apenas com o desafio. Redirecionamentos, middleware de autenticação, reescritas de localidade ou um wrapper JSON podem interromper a verificação. Confirme se a variável de ambiente foi carregada pelo processo de desenvolvimento em execução.

A verificação foi bem-sucedida, mas nenhuma mensagem chega

A verificação de retorno de chamada por si só não inscreve a conta do WhatsApp Business nos campos. Confirme a assinatura messages no painel e se o número de telefone pertence ao aplicativo e à conta esperados. Envie uma mensagem de um destinatário permitido se o aplicativo ainda estiver em modo de desenvolvimento.

Cada POST falha na validação de assinatura

As causas comuns são o uso do token de acesso em vez do segredo do aplicativo, o hash do JSON analisado, a omissão do prefixo sha256= ou a comparação de codificações diferentes. Registre o comprimento do corpo e se o cabeçalho existe, mas nunca imprima o segredo ou a carga completa do cliente.

As mensagens de texto funcionam, mas o manuseio da mídia falha.

As notificações de mídia contêm um ID, não necessariamente os bytes do arquivo. Busque mídia por meio da API Graph com um token de acesso válido e faça download dela. Mantenha esse fluxo de trabalho mais lento fora do caminho de confirmação do webhook.

O endpoint local vê eventos duplicados

Inspecione o status e a latência da resposta, adicione idempotência durável e reproduza um evento capturado após cada correção. O guia de repetição do webhook mostra como evitar o envio de uma nova mensagem real a cada alteração de código.

Proteja os dados do cliente durante testes locais

  • Use números de telefone de teste e conversas sintéticas sempre que possível.
  • Edite números de telefone, corpos de mensagens, URLs de mídia, contatos e nomes de perfil de logs.
  • Armazene o segredo do aplicativo, tokens de acesso e verifique o token apenas em arquivos de ambiente ignorados ou em um gerenciador de segredos.
  • Restringir quem pode visualizar capturas de túnel e excluí-las após a sessão de depuração.
  • Validar identificadores de objetos, campos e contas antes de executar ações de negócios.

Um túnel torna a iteração rápida, mas também traz formato de produção dados pessoais para uma máquina de desenvolvedor. Aplique os controles da lista de verificação de segurança do túnel antes de testar com usuários reais.

Perguntas frequentes

Como faço para testar um webhook da API do WhatsApp Cloud no localhost?
Execute seu gerenciador de webhook localmente, exponha-o com um túnel HTTPS, registre o URL de retorno de chamada público e token de verificação no Meta, assine as mensagens e, em seguida, envie uma mensagem de teste.
O que um endpoint de verificação de webhook do WhatsApp deve retornar?
Para uma solicitação GET válida em que hub.mode é subscribe e hub.verify_token corresponde, retorne o valor hub.challenge como texto simples com HTTP 200.
Como verifico solicitações POST de webhook do WhatsApp?
Calcule HMAC-SHA256 sobre o corpo exato da solicitação com o Meta App Secret, prefixe o resumo hexadecimal com sha256= e compare-o com segurança de tempo com X-Hub-Signature-256.
Por que meu webhook verificado do WhatsApp não recebe eventos?
A verificação de retorno de chamada não inscreve automaticamente todos os campos. Confirme se a conta comercial do WhatsApp está inscrita nas mensagens e se o remetente de teste e o número de telefone estão disponíveis para o aplicativo.