Dev

Enviar WhatsApp com Node.js em 5 minutos

Dá para enviar uma mensagem de WhatsApp pelo Node.js só com o fetch nativo: sem biblioteca de WhatsApp e sem navegador headless. Leva uns cinco minutos se você já tem um número conectado. Transparência: a ZapZap API é não oficial e conecta um número comum por QR code. Isso significa que está fora dos termos do WhatsApp e que números podem ser limitados ou banidos, então leia a lista do final antes de enviar para gente de verdade.

Publicado em · 4 min de leitura

Este artigo também está disponível em: English · Español

0. O que você precisa (2 minutos)

1) Uma conta na ZapZap e uma instância conectada por QR code. Contas novas recebem crédito de boas-vindas ao verificar o telefone, conforme a campanha vigente; o início rápido mostra onde clicar. 2) Sua API key, seu API secret e o ID da instância. 3) Node 18 ou mais novo (node -v).

Coloque as credenciais em variáveis de ambiente, nunca no código. A autenticação são dois cabeçalhos em toda requisição, x-api-key e x-api-secret.

Shell

export ZAPZAP_INSTANCE_ID="your-instance-id"
export ZAPZAP_API_KEY="your-api-key"
export ZAPZAP_API_SECRET="your-api-secret"

1. Enviar um texto (1 minuto)

Crie o arquivo send.mjs com o código abaixo e rode com node send.mjs. A extensão .mjs habilita await no nível do arquivo; em CommonJS, envolva a chamada numa função async. Use o seu próprio número primeiro. O número vai com DDI e só dígitos, sem +, espaços ou traços.

Node.js: send.mjs

const BASE = "https://api.zapzapapi.com/api/v1";
const { ZAPZAP_INSTANCE_ID, ZAPZAP_API_KEY, ZAPZAP_API_SECRET } = process.env;

async function zapzap(path, body) {
  const res = await fetch(BASE + "/" + ZAPZAP_INSTANCE_ID + path, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": ZAPZAP_API_KEY,
      "x-api-secret": ZAPZAP_API_SECRET,
    },
    body: JSON.stringify(body),
    signal: AbortSignal.timeout(20_000), // never leave a call hanging
  });
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error("ZapZap " + res.status + ": " + JSON.stringify(data));
  return data;
}

// number: country code + digits only (no +, spaces or dashes)
console.log(await zapzap("/send/text", { number: "5511999999999", text: "Hello from Node.js" }));

2. Enviar um PDF ou uma imagem (1 minuto)

Arquivos passam por /send/media. O campo type escolhe o tipo (image, video, audio, ptt, document, sticker), file é uma URL pública, text é a legenda e docName é o nome do arquivo nos documentos. O arquivo precisa estar acessível por uma URL pública; se ele está no seu disco, suba para um armazenamento e use um link temporário.

Node.js

await zapzap("/send/media", {
  number: "5511999999999",
  type: "document",
  file: "https://example.com/invoice-1042.pdf",
  text: "Your invoice",
  docName: "invoice-1042.pdf",
});

3. Receber respostas com Express (1 minuto)

Registre a sua URL pública como webhook da instância com o evento de mensagens ligado (veja configurar webhook). No desenvolvimento local você precisa de um túnel, porque a API precisa alcançar você pela internet.

Descarte sempre os eventos fromMe, ou uma resposta automática responde a si mesma em loop. Confira o formato exato com uma mensagem real primeiro; para validar o cabeçalho X-ZapZap-Signature e eliminar entregas repetidas, siga webhooks em produção.

Node.js: Express

import express from "express";

const app = express();
app.use(express.json());

app.post("/webhook", (req, res) => {
  res.sendStatus(200); // answer first, process after

  const msg = req.body?.data?.message;
  if (!msg || msg.fromMe) return; // ignore your own messages

  console.log("from " + msg.chatid + ": " + msg.text);
  // reply, save to a database, call an AI agent...
});

app.listen(3000, () => console.log("listening on :3000"));

4. Repita só os erros certos

Tente de novo apenas em 429 e 5xx, com esperas crescentes. Um 400, 401 ou 403 significa que a requisição ou as credenciais estão erradas, e repetir só produz mais erros.

Node.js

async function withRetry(fn, tries = 4) {
  for (let i = 0; ; i++) {
    try {
      return await fn();
    } catch (err) {
      const retryable = /ZapZap (429|5\d\d)/.test(err.message);
      if (!retryable || i >= tries - 1) throw err;
      await new Promise((r) => setTimeout(r, 2 ** i * 1000));
    }
  }
}

await withRetry(() => zapzap("/send/text", { number: "5511999999999", text: "Retried send" }));

5. Antes de enviar para gente de verdade

Credenciais só em variáveis de ambiente, nunca no git. Timeout em toda chamada e retry só em 429 e 5xx. Em listas, envie um por vez com uma pausa aleatória entre as mensagens em vez de disparar tudo em paralelo; trate os valores da pausa como um ponto de partida seu, não como regra do WhatsApp, e para campanhas grandes a fila do servidor tira isso do seu app. Envie só para quem concordou em receber e respeite quem pediu para sair.

Não oficial significa risco de bloqueio. Ritmo, aquecimento e listas limpas reduzem, nada elimina. Use um número que você possa perder enquanto testa e leia o guia anti-bloqueio. Se você precisa de canal homologado, veja API oficial x ZapZap. Para planejar: R$15 por número ativo por mês, pré-pago, sem cobrança por mensagem (preços).

Lista completa de endpoints na referência da API e no arquivo OpenAPI. Prefere outra linguagem? Os mesmos endpoints funcionam em Python e nos outros guias do blog, ou sem código no n8n. Para a versão com mais detalhes deste guia, veja enviar WhatsApp com Node.js. Os SDKs oficiais ainda não estão publicados no npm nem no PyPI; o código-fonte está no GitHub em zapzap-sdk-node, zapzap-sdk-python e zapzap-sdk-php, e a API REST funciona direto com o fetch, como acima.

Resumo rápido

  • O fetch nativo do Node 18 basta: um POST em /send/text com x-api-key e x-api-secret.
  • Arquivos vão por /send/media com uma URL pública; respostas chegam por webhook, e eventos fromMe devem ser descartados.
  • Repita só 429 e 5xx, com espera crescente. 400, 401 e 403 são erro de requisição ou de credencial.
  • Os SDKs ainda não estão no npm ou PyPI (repositórios no GitHub); a API REST funciona direto, sem biblioteca.

Perguntas frequentes

Preciso de alguma biblioteca para enviar WhatsApp pelo Node.js?

Não. O fetch nativo do Node 18 ou mais novo faz o POST em /send/text. O Express só entra se você quiser receber respostas por webhook.

Por que o meu bot responde a si mesmo em loop?

Porque o webhook também entrega as mensagens que você mesmo enviou. Descarte os eventos em que fromMe é verdadeiro antes de responder.

Existe um SDK da ZapZap para Node.js no npm?

Ainda não está publicado no npm. O código está em github.com/zapzapapi/zapzap-sdk-node, e você pode usar a API REST direto com o fetch, como neste guia.

Enviar por esta API pode bloquear o meu número?

Pode. A API é não oficial e vai contra os termos do WhatsApp. Ritmo, aquecimento, opt-in e listas limpas reduzem o risco, mas nada o elimina. Teste com um número que você possa perder.

Mande a primeira mensagem agora

Crie a conta, conecte um número de teste por QR code e rode o send.mjs. R$15 por número ativo ao mês, sem cobrança por mensagem.