Capítulo 4 de 15

Receber a mensagem: o webhook, com o código pronto

O endereço que escuta o WhatsApp, com código.

  • Leitura: 1 min
  • Custo para acompanhar: R$ 0
  • Cadastro: nenhum

Manual gratuito de Certu · Atualizado em 15 de setembro de 2026 · ver o manual inteiro

Como receber mensagens do WhatsApp no meu servidor?

Resposta curta

Um endereço https que responde a duas chamadas: a verificação por GET, devolvendo o desafio da Meta, e as mensagens por POST. Ele confere a assinatura e responde 200 rápido.

O código

Salve como src/index.js.

src/index.jsjavascript
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    if (url.pathname !== '/webhook') return new Response('no ar');

    // Verificacao: a Meta chama uma vez, quando voce cadastra o endereco.
    if (request.method === 'GET') {
      const ok = url.searchParams.get('hub.mode') === 'subscribe'
        && url.searchParams.get('hub.verify_token') === env.VERIFY_TOKEN;
      return ok
        ? new Response(url.searchParams.get('hub.challenge'))
        : new Response('token errado', { status: 403 });
    }

    if (request.method !== 'POST') return new Response('metodo nao permitido', { status: 405 });

    const corpo = await request.text();
    const assinatura = request.headers.get('x-hub-signature-256');
    if (!(await assinaturaConfere(corpo, assinatura, env.APP_SECRET))) {
      return new Response('assinatura invalida', { status: 401 });
    }

    // Confirmacao de entrega chega aqui tambem, sem messages. Ignore.
    const valor = JSON.parse(corpo)?.entry?.[0]?.changes?.[0]?.value;
    const mensagem = valor?.messages?.[0];
    if (mensagem) console.log('mensagem de', mensagem.from, mensagem.text?.body);
    return new Response('ok');
  },
};

// Sem esta conferencia, qualquer um manda mensagem falsa pro seu endereco.
async function assinaturaConfere(corpo, cabecalho, segredo) {
  if (!cabecalho || !segredo) return false;
  const chave = await crypto.subtle.importKey(
    'raw', new TextEncoder().encode(segredo),
    { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'],
  );
  const assinado = await crypto.subtle.sign('HMAC', chave, new TextEncoder().encode(corpo));
  const esperado = 'sha256=' + [...new Uint8Array(assinado)]
    .map((b) => b.toString(16).padStart(2, '0')).join('');

  // Comparacao de tempo constante.
  if (esperado.length !== cabecalho.length) return false;
  let diferenca = 0;
  for (let i = 0; i < esperado.length; i += 1) {
    diferenca |= esperado.charCodeAt(i) ^ cabecalho.charCodeAt(i);
  }
  return diferenca === 0;
}

Publicar

VERIFY_TOKEN é uma senha sua. APP_SECRET fica em Configurações, Básico, no portal de desenvolvedores.

terminalbash
npm create cloudflare@latest atendente -- --type=hello-world
cd atendente
npx wrangler secret put VERIFY_TOKEN
npx wrangler secret put APP_SECRET
npx wrangler deploy

Cadastrar na Meta

  1. Passo 1

    Cole o endereço

    Com /webhook no fim e a senha de VERIFY_TOKEN.

  2. Passo 2

    Assine o campo messages

    Sem isso nenhuma mensagem chega.

  3. Passo 3

    Teste

    Mande uma mensagem e veja com npx wrangler tail.

Pergunta frequente

Preciso mesmo conferir a assinatura?

Sim. Sem ela qualquer um manda mensagem falsa e você paga a IA.

E onde entra Certu

Em Certu esta parte é invisível e cuidada todo mês.

Montar um agente e testar sem cartão