WPPAPI
Guia · Backend

Receber mensagens de WhatsApp em Node.js

Um receptor de webhooks pronto para produção: Express, validação HMAC com comparação em tempo constante e as armadilhas que derrubam todo mundo.

Como a assinatura funciona

Cada entrega de webhook da WPPAPI inclui dois headers: X-WPPAPI-Signature — o HMAC-SHA256 do body em hexadecimal, calculado com o secret do seu webhook — e X-WPPAPI-Signature-Alg: sha256. Os headers X-WPPAPI-Event e X-WPPAPI-Instance identificam o evento e a instância. Recalcule o HMAC sobre os bytes exatos recebidos e compare com timingSafeEqual. Se bater, a requisição veio da WPPAPI e não foi alterada.

O receptor completo

import express from "express";
import { createHmac, timingSafeEqual } from "crypto";

const app = express();
const SECRET = process.env.WPPAPI_WEBHOOK_SECRET;

// Importante: o HMAC é calculado sobre os BYTES EXATOS do body.
// Use express.raw() na rota do webhook — não express.json().
app.post(
  "/webhooks/wppapi",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = req.header("X-WPPAPI-Signature") ?? "";
    const expected = createHmac("sha256", SECRET)
      .update(req.body)
      .digest("hex");

    const valid =
      signature.length === expected.length &&
      timingSafeEqual(Buffer.from(signature), Buffer.from(expected));

    if (!valid) return res.status(401).send("invalid signature");

    const event = JSON.parse(req.body.toString("utf8"));

    if (event.event === "received" && !event.data?.fromMe) {
      console.log("Mensagem de", event.data.from, ":", event.data.body);
      // Enfileire o processamento pesado e responda 200 rápido.
    }

    res.sendStatus(200);
  }
);

app.listen(3000);

Em desenvolvimento, exponha a porta local com um túnel (ngrok, cloudflared) e cadastre a URL no painel da WPPAPI. O secret é exibido ao criar o webhook.

As 4 regras de produção

Responda 200 em menos de 5 segundos

Valide a assinatura, enfileire e responda. Processamento lento causa timeout e reentrega — seu handler precisa ser idempotente de qualquer forma.

Use o raw body, sempre

O erro nº 1 em validação HMAC: deixar o express.json() fazer parse e depois re-serializar. Bytes diferentes = assinatura inválida. Use express.raw() na rota do webhook.

Confie no retry, prepare-se para duplicatas

A WPPAPI reentrega em caso de falha e move para dead-letter queue após esgotar as tentativas. Guarde IDs processados para descartar duplicatas.

Filtre fromMe

Eventos das suas próprias mensagens chegam com fromMe: true. Se seu bot responde mensagens, filtre — ou ele conversará consigo mesmo.

Com o receptor rodando, o próximo passo natural é automatizar respostas no n8n ou plugar o Chatwoot para atendimento humano. A referência de eventos está na documentação.

Teste com eventos reais

3 dias grátis, sem cartão — webhooks com HMAC inclusos em todos os planos.

Criar conta grátis