Ambiente de código e hospedagem

WhatsApp no Replit: receptor de webhook com assinatura, Secrets e Reserved VM

O Replit é um ambiente de desenvolvimento no navegador com hospedagem. A documentação (lida em 11/11/2026) explica que cada Secret vira uma variável de ambiente do projeto e que a publicação pode ser Autoscale, Static, Reserved VM ou Scheduled. Para a ZapZap, o ponto forte é rodar um servidor que recebe webhooks e responde mensagens.

Atualizado em

Por que conectar

O Replit é bom para o lado que as ferramentas de front não cobrem: um servidor sempre disponível que recebe cada evento da ZapZap, confere a assinatura, evita processar o mesmo evento duas vezes e aciona a sua lógica ou um modelo de IA. A ZapZap conecta números por um canal não oficial: o WhatsApp pode limitar ou bloquear qualquer número, então ritmo, consentimento e aquecimento contam. São R$15 por número ativo/mês, pré-pago, sem cobrança por mensagem, e você começa com crédito de boas-vindas ao verificar o telefone, sujeito à campanha vigente.

Passo a passo

  1. 1

    Crie o projeto e cadastre os Secrets

    No editor do projeto, abra Tools > Secrets (em Setup), clique em New Secret e crie ZAPZAP_API_KEY e ZAPZAP_API_SECRET (de Settings > API em app.zapzapapi.com). Eles ficam disponíveis como variáveis de ambiente. A documentação avisa que quem tem acesso ao projeto, e colaboradores, podem ver os valores: não convide quem não deva vê-los.

  2. 2

    Suba o receptor de webhook

    Cole o servidor do exemplo em server.mjs e rode. Ele lê o corpo bruto, valida X-ZapZap-Signature (t=<unix>,v1=<hmac_hex>, HMAC-SHA256 sobre t + ponto + corpo, tolerância de 5 minutos), responde 200 rápido e guarda o delivery_id para ignorar repetições.

  3. 3

    Escolha o tipo de publicação

    Para um receptor que precisa estar sempre ligado, a documentação do Replit descreve Reserved VM como recursos constantes para um app que roda continuamente. Autoscale ajusta recursos ao uso: confira se o seu volume de eventos tolera o tempo de partida.

  4. 4

    Cadastre o webhook na instância

    Chame PUT https://api.zapzapapi.com/api/v1/instances/{id}/webhook com webhook_url (HTTPS) e events (por exemplo, messages). A resposta traz o webhook_secret (whsec_...): salve como Secret ZAPZAP_WEBHOOK_SECRET. Teste com POST /instances/{id}/webhook/test.

  5. 5

    Responda pela API

    Para responder, chame POST /{instanceId}/send/text com number e text depois de ter respondido 200 ao webhook. O envelope traz delivery_id, event_id, instance_id, instance_name, event e data.

  6. 6

    Depure as entregas

    GET /instances/{id}/webhook/deliveries mostra as entregas recentes. Se o seu servidor falhar, a ZapZap tenta de novo com intervalos crescentes, por isso a deduplicação por delivery_id importa.

Exemplo

// server.mjs: receives the ZapZap webhook, checks the signature and ignores repeated deliveries.
// Replit Secrets: ZAPZAP_WEBHOOK_SECRET (the whsec_ value returned when you set the webhook)
import { createServer } from "node:http";
import { createHmac, timingSafeEqual } from "node:crypto";

const seen = new Set();

function valid(raw, header) {
  const p = Object.fromEntries(String(header || "").split(",").map((kv) => kv.split("=")));
  if (!p.t || !p.v1 || Math.abs(Date.now() / 1000 - Number(p.t)) > 300) return false;
  const mac = createHmac("sha256", process.env.ZAPZAP_WEBHOOK_SECRET).update(p.t + "." + raw).digest("hex");
  return mac.length === p.v1.length && timingSafeEqual(Buffer.from(mac), Buffer.from(p.v1));
}

createServer((req, res) => {
  let raw = "";
  req.on("data", (c) => (raw += c));
  req.on("end", () => {
    if (req.method !== "POST" || !valid(raw, req.headers["x-zapzap-signature"])) {
      res.writeHead(401).end();
      return;
    }
    const env = JSON.parse(raw);
    res.writeHead(200).end("ok"); // answer fast, work afterwards
    if (seen.has(env.delivery_id)) return;
    seen.add(env.delivery_id);
    console.log("event", env.event, "delivery", env.delivery_id);
  });
}).listen(process.env.PORT || 3000, "0.0.0.0");

Ilustrativo. Os campos e endpoints exatos estão na documentação.

Tipos de publicação do Replit, segundo a documentação (11/11/2026)

TipoO que a documentação dizServe para receber webhook?
Reserved VMRecursos consistentes para um app que roda continuamenteSim, é o encaixe natural
AutoscaleAjusta recursos conforme o usoPode servir; teste o tempo de partida
StaticSites que não mudam conforme a entrada do usuárioNão: não roda servidor
ScheduledRoda em horários que você escolherNão para webhook; serve para rotinas

Perguntas frequentes

Como os Secrets do Replit chegam ao código?

Como variáveis de ambiente, lidas com process.env no Node.js. A documentação lida em 11/11/2026 também avisa que colaboradores com acesso podem ver os valores.

Preciso validar a assinatura?

Sim, se a URL for pública. Sem isso, qualquer pessoa pode simular eventos. A validação usa o webhook_secret devolvido ao configurar o webhook.

Por que o mesmo evento pode chegar duas vezes?

Se o seu servidor não responder com sucesso, a ZapZap tenta de novo. Guarde o delivery_id e ignore repetições.

O Replit serve para campanhas em massa?

O ambiente serve, mas a ZapZap é uma solução não oficial e disparo em massa e mensagens idênticas em sequência aumentam o risco de bloqueio. Envie só a quem tem relação com você.

Quanto custa a ZapZap?

São R$15 por número ativo/mês, pré-pago, sem cobrança por mensagem, e você começa com crédito de boas-vindas ao verificar o telefone, sujeito à campanha vigente.

Comece agora

Crie sua conta, conecte um número em minutos e teste a API com aquecimento e anti-bloqueio. R$15 por número ativo/mês, pré-pago, sem fidelidade.