Documentação

Boas práticas, fluxo da fila e snippets de API

Aviso de risco: esta integração usa biblioteca não oficial. A Meta proíbe automação não-oficial em seus termos. Para uso comercial em escala, considere a WhatsApp Cloud API oficial — ou avise seu cliente do risco de banimento por contrato.

Boas práticas obrigatórias

  1. Throttle entre envios. Já configurado: SEND_THROTTLE_MIN_MS/SEND_THROTTLE_MAX_MS (padrão 2-5s com jitter). Para disparo, eleve para 10-30s.
  2. Aqueça números novos. Use o número 2+ semanas com tráfego natural (foto, status, conversas reais) antes de qualquer disparo. Volume inicial: 50-100 msg/dia, escalar gradualmente.
  3. Varie o conteúdo. Mesma mensagem para 100+ destinatários é o sinal nº 1 de spam. Personalize com nome e mude pequenas palavras (use templates com variáveis).
  4. Prefira contatos opt-in. Reportes de "spam" pelos destinatários é o gatilho mais forte. Só envie pra quem aceitou receber. Lista fria de cold-mailing → ban quase certo.
  5. Respeite horário comercial. Disparos de madrugada multiplicam reportes. Use 9h-18h, dias úteis.
  6. Evite mensagens só com link/promoção. Texto curto + URL é detectado pelos filtros do WhatsApp. Acrescente contexto humano antes do link.
  7. Não desligue o celular pareado. O whatsapp-web.js depende do celular. Manter o aparelho off por dias degrada a sessão.
  8. Reaja a sinais de bloqueio. Se receber erro Rate limit ou as mensagens pararem de chegar, pare imediatamente e deixe descansar 24h. Insistir = ban definitivo.
  9. Múltiplos números, não um só. Distribua disparos entre várias sessões (cada uma com seu chip). Concentrar volume em 1 número = ban garantido.
  10. Monitore o delivery rate. Queda repentina nas confirmações de entrega = você foi shadow-banned. Reduza volume na hora.

Todo envio passa por uma fila Redis (BullMQ). Isso garante ordem, retries automáticos e que mensagens não são perdidas em caso de crash.

Estados do job
waitingAguardando worker. Se a sessão estiver desconectada, fica aqui até reconectar.
activeWorker processando o envio agora.
completedMensagem entregue ao WhatsApp. Retorna messageId.
failedEsgotou tentativas (3x com backoff exponencial). Ver no Bull Board.
delayedAguardando backoff antes do próximo retry.

Como funciona o aguardando envio

  1. Você chama POST /sessions/:id/send-message → job entra em waiting.
  2. O worker da sessão (1 por sessão, concurrency=1) puxa o próximo job.
  3. Se a sessão estiver connected, o envio acontece. Se não, o job fica em waiting até a sessão reconectar.
  4. Após o envio, o worker dorme entre SEND_THROTTLE_MIN_MS e SEND_THROTTLE_MAX_MS (jitter randômico) antes de pegar o próximo.
  5. Falhas com backoff exponencial (2s, 4s, 8s) e até 3 tentativas. Erros marcados como permanentes (ex: número inválido) não retentam.
Default = fire-and-forget. Toda chamada enfileira o job e responde 202 com jobId em milissegundos — o throttle anti-ban roda no worker, não na sua chamada HTTP. Acompanhe o resultado pelo Bull Board ( Filas). Use wait:true apenas em testes pra bloquear até concluir (limite SEND_JOB_TIMEOUT_MS).
1. Login — recebe um cookie de sessão.
curl -X POST http://localhost:9002/auth/login \
  -H "Content-Type: application/json" \
  -c cookie.txt \
  -d '{"username":"admin","password":"admin"}'
2. Enviar mensagem (default) — enfileira e responde 202 na hora com jobId.
curl -X POST http://localhost:9002/sessions/<SESSION_ID>/send-message \
  -H "Content-Type: application/json" \
  -b cookie.txt \
  -d '{"number":"5511999999999","message":"olá"}'

Resposta: { "queued": true, "jobId": "...", "sessionReady": true }

3. Aguardar confirmação (raro) — só use em testes ou quando precisar do messageId imediato. Não use em produção.
curl -X POST http://localhost:9002/sessions/<SESSION_ID>/send-message \
  -H "Content-Type: application/json" \
  -b cookie.txt \
  -d '{"number":"5511999999999","message":"olá","wait":true}'
4. Server-to-server — sem login, ideal para n8n / Zapier / scripts.
curl -X POST http://localhost:9002/sessions/<SESSION_ID>/send-message \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"number":"5511999999999","message":"olá"}'
5. Consultar status do job — verifica o que aconteceu com um envio enfileirado.
curl http://localhost:9002/sessions/<SESSION_ID>/jobs/<JOB_ID> \
  -H "X-API-Key: $API_KEY"

Resposta com state = waiting | active | completed | failed | delayed, result.messageId se concluído, error se falhou.

6. Webhook · mensagem recebida — payload da sua URL.
// 1-pra-1
{
  "sessionId": "abc12345",
  "messageId": "true_5511...@c.us_3EB0XYZ",
  "from":        "5511999999999@c.us",  // remetente
  "author":      "5511999999999@c.us",  // = from
  "authorPhone": "5511999999999",       // telefone E164 (sem @)
  "authorName":  "João Silva",          // pushname/contato
  "to":          "5511888888888@c.us",
  "body": "olá, tudo bem?",
  "type": "chat", "fromMe": false, "isGroup": false,
  "timestamp": 1714150000
}

// grupo
{
  "sessionId": "abc12345",
  "messageId": "false_120363xyz@g.us_ABC",
  "from":        "120363xyz@g.us",      // ID do grupo
  "author":      "199570051076163@lid", // id opaco (LID) ou @c.us
  "authorPhone": "5511999999999",       // ← telefone REAL resolvido
  "authorName":  "João Silva",
  "to":          "5511888888888@c.us",
  "body": "alguém viu o relatório?",
  "type": "chat", "fromMe": false, "isGroup": true,
  "timestamp": 1714150000
}
Mídia por URL — sem precisar subir arquivo no servidor. Aceita imagem, vídeo, áudio, doc.
curl -X POST http://localhost:9002/sessions/<ID>/send-message \
  -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
  -d '{
    "number": "5511999999999",
    "media_url": "https://exemplo.com/imagem.jpg",
    "message": "Legenda opcional"
  }'
Reply (citar mensagem) — passe o messageId em reply_to.
curl -X POST http://localhost:9002/sessions/<ID>/send-message \
  -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
  -d '{
    "number": "5511999999999",
    "message": "Concordo!",
    "reply_to": "true_5511...@c.us_3EB0XYZ"
  }'
Mention em grupo — texto inclui @5511... e o array mentions com os mesmos IDs.
curl -X POST http://localhost:9002/sessions/<ID>/send-group-message \
  -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
  -d '{
    "group_id": "120363xyz@g.us",
    "message": "Atenção @5511999999999, ver isto.",
    "mentions": ["5511999999999@c.us"]
  }'
Humanizar envio — typing por X ms, marca o chat como lido depois.
curl -X POST http://localhost:9002/sessions/<ID>/send-message \
  -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
  -d '{
    "number": "5511999999999",
    "message": "Olá, tudo bem?",
    "simulate_typing_ms": 3000,
    "mark_chat_read": true
  }'
Enquete (Poll) — 2 a 12 opções; multiple_answers permite múltipla escolha.
curl -X POST http://localhost:9002/sessions/<ID>/send-poll \
  -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
  -d '{
    "number": "5511999999999",
    "name": "Almoço onde?",
    "options": ["Pizza", "Sushi", "Hamburger"],
    "multiple_answers": false
  }'
Reagir a mensagem — emoji direto, vazio ("") remove a reação.
curl -X POST http://localhost:9002/sessions/<ID>/messages/<MSG_ID>/react \
  -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \
  -d '{ "emoji": "👍" }'
Marcar chat como lido — útil pra esconder o "não-lido" do celular do cliente.
curl -X POST http://localhost:9002/sessions/<ID>/chats/<CHAT_ID>/mark-read \
  -H "X-API-Key: $API_KEY"
Verificar assinatura do webhook — quando WEBHOOK_SECRET está setado, todo payload vem com header X-Whatshd-Signature: sha256=<hex>. Compare com HMAC do body pra garantir autenticidade.
// Node.js — Express
const crypto = require('crypto');

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.header('x-whatshd-signature');
  const expected = 'sha256=' + crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');
  if (sig !== expected) return res.sendStatus(401);

  const payload = JSON.parse(req.body.toString());
  // ... processa o evento
  res.sendStatus(200);
});
Healthcheck profundo — pra UptimeRobot, Grafana, Kubernetes liveness/readiness.
curl https://whats.hathdata.com/healthz   # 200 ok / 503 down

# Resposta:
{
  "status": "ok",          // ok | degraded | down
  "uptime": 3600,
  "checkLatencyMs": 4,
  "checks": {
    "redis":    { "ok": true, "latencyMs": 1 },
    "sessions": { "total": 5, "connected": 4, "unpaired": 0, ... },
    "queue":    { "totalWaiting": 12, "totalFailed": 0, "totalActive": 1 }
  }
}

# Liveness simples (sempre 200 enquanto processo Node estiver vivo):
curl https://whats.hathdata.com/livez
Status de entrega (delivery acks) — quando WEBHOOK_DELIVERY_ACKS=true, sua URL recebe estes payloads conforme a mensagem é entregue/lida.
{
  "type": "message.delivered",  // ou "message.read"
  "sessionId": "abc12345",
  "messageId": "true_5511...@c.us_3EB0XYZ",
  "to": "5511999999999@c.us",
  "ack": 3,                      // 3=delivered, 4=read, 5=played
  "timestamp": 1714150033
}
Abrir Bull Board

Whats.hd

Acesse o painel para gerenciar suas sessões

Whats.hd
painel de sessões
Filas
Sessões
0
Conectadas
0
Pendentes na fila
0
Recebidas
0
Comece conectando seu primeiro número
Crie uma sessão, escaneie o QR Code e comece a enviar e receber mensagens.
Whats.hd · sessões em paralelo · fila persistente · webhook