API em Node.js/TypeScript para gerenciar sessões do WhatsApp Web usando WPPConnect, com:
- Criação/recuperação de sessões (token store em disco)
- QR Code (dataURL e PNG)
- Envio de mensagens (texto e mídia)
- Webhooks de eventos (mensagens, ACKs, status)
- Paginação/listagem de conversas (IDs de chats)
- Reconexão manual/sob demanda (sem retry agressivo automático)
- Documentação Swagger em
/docs - Pronto para Docker (prod e dev)
Aviso: Este projeto usa WhatsApp Web (não é a API oficial do WhatsApp). Use com responsabilidade e de acordo com os termos de serviço da plataforma.
- Arquitetura
- Requisitos
- Configuração
- Variáveis de Ambiente
- Instalação local
- Rodando em Docker (profiles)
- Como usar
- Autenticação via API Key
- Swagger
- Rotas
- Exemplos com cURL
- Webhooks
- Persistência e limpeza
- Boas práticas
- Solução de problemas
- Express + TypeScript
- WPPConnect (Puppeteer/Chromium)
- Persistência de sessão em disco (
SESSIONS_DIR/<sessionId>) - Pino (logs)
- Swagger UI (
/docs,/openapi.json) - Docker (chromium instalado)
- Node.js 18+ (recomendado 20+)
- npm 9+
- Docker (opcional, recomendado em produção)
- Chrome/Chromium local (apenas fora do Docker)
Crie um .env na raiz (ou configure no provedor):
NODE_ENV=production
PORT=3000
SESSIONS_DIR=./sessions
API_KEY=change-me
HEADLESS=true
CHROME_EXECUTABLE_PATH=
CHROME_EXTRA_ARGS=--no-sandbox,--disable-setuid-sandbox,--disable-dev-shm-usage,--disable-gpu
WEBHOOK_TIMEOUT_MS=6000
WEBHOOK_MAX_RETRIES=3
MAX_CONCURRENT_SESSIONS=0
BOOTSTRAP_READY_TIMEOUT_MS=180000
DESTROY_MAX_RETRIES=5
READY_FALLBACK_MS=30000
WWEB_READY_CHECK_TIMEOUT_MS=60000
Notas rápidas:
SESSIONS_DIR: raiz das pastas de sessão; o LocalAuth cria tambémsession-<id>.HEADLESS: mantenha true em produção.CHROME_EXECUTABLE_PATH: deixe vazio no Windows/local; no Docker já usamos chromium do container.READY_FALLBACK_MS: se o eventoreadynão ocorrer, promovemos a READY quandogetState()ficar CONNECTED por esse tempo (com injeção do wwebjs pronta).WWEB_READY_CHECK_TIMEOUT_MS: tempo máximo para considerar a injeção concluída antes de operações comogetChats/sendMessage.
npm i
npm run dev
# Swagger: http://localhost:3000/docsdocker-compose.yml inclui dois serviços e perfis:
- prod:
api(build runtime, porta${PORT}) - dev:
api-dev(ts-node-dev, porta${DEV_PORT:-3001}->${PORT})
Comandos:
# Produção
docker compose --profile prod up -d --build
# Desenvolvimento
docker compose --profile dev up -d --build
# Logs
docker compose logs -f api
docker compose logs -f api-devImportante: não suba api e api-dev simultaneamente no mesmo host/volume.
Se API_KEY estiver definida, envie:
x-api-key: <sua-chave>
- UI:
GET /docs - JSON:
GET /openapi.json
| Método | Path | Descrição |
|---|---|---|
| GET | /health |
Healthcheck (público) |
| POST | /sessions |
Criar sessão { "sessionId": "MINHA-SESSAO" } |
| GET | /sessions |
Listar sessões ativas no processo |
| GET | /sessions/:id/status |
Status de uma sessão |
| GET | /sessions/:id/qr |
QR Code em dataUrl (?wait=1&timeout=10000) |
| GET | /sessions/:id/qr.png |
QR Code como PNG (?w=350&wait=1&timeout=10000) |
| POST | /sessions/:id/webhook |
Definir/remover webhook { "url": "https://..." } |
| DELETE | /sessions/:id?deleteData=true |
Destruir sessão; com deleteData=true apaga tudo (pasta raiz e caches session-*) |
| GET | /sessions/:id/chats?page=1&limit=10 |
Lista IDs de conversas com paginação |
| POST | /messages/send |
Enviar texto ou mídia (por chatId ou to) |
| GET | /messages/:sessionId/history |
Listar mensagens do chat; body { "chatId": "...@c.us" } (cada item inclui dateSent) |
| GET | /messages/:sessionId/resolve |
Resolver número para chatId (phone em query/body; prefixa 55 se faltar) |
Padrão de resposta JSON: sempre traz message (ex.: ok, pending, initializing, no_whatsapp, error).
Criar sessão
curl -X POST http://localhost:3000/sessions \
-H "Content-Type: application/json" \
-H "x-api-key: change-me" \
-d '{ "sessionId": "ROBOCELL1" }'Obter QR (dataURL) aguardando até 10s
curl "http://localhost:3000/sessions/ROBOCELL1/qr?wait=1&timeout=10000" -H "x-api-key: change-me"Obter QR (PNG 400px)
curl -L "http://localhost:3000/sessions/ROBOCELL1/qr.png?w=400&wait=1&timeout=10000" \
-H "x-api-key: change-me" --output qr.pngListar chats
curl "http://localhost:3000/sessions/ROBOCELL1/chats?page=1&limit=10" -H "x-api-key: change-me"Enviar texto por chatId
curl -X POST http://localhost:3000/messages/send \
-H "Content-Type: application/json" -H "x-api-key: change-me" \
-d '{ "sessionId":"ROBOCELL1", "chatId":"554888211762@c.us", "type":"text", "body":"Olá!" }'Enviar documento por URL com legenda
curl -X POST http://localhost:3000/messages/send \
-H "Content-Type: application/json" -H "x-api-key: change-me" \
-d '{ "sessionId":"ROBOCELL1", "to":"554888211762", "type":"media",
"media": { "url":"https://example.com/contrato.pdf", "mimetype":"application/pdf", "filename":"contrato.pdf" },
"caption":"segue o contrato" }'Resolver número para chatId (prefixa 55)
curl -G "http://localhost:3000/messages/ROBOCELL1/resolve" \
-H "x-api-key: change-me" --data-urlencode "phone=48988211762"Histórico de mensagens (inclui dateSent)
curl -X GET "http://localhost:3000/messages/ROBOCELL1/history" \
-H "x-api-key: change-me" -H "Content-Type: application/json" \
-d '{ "chatId": "554888211762@c.us" }'Destruir sessão e apagar tudo (inclusive caches session-*)
curl -X DELETE "http://localhost:3000/sessions/ROBOCELL1?deleteData=true" -H "x-api-key: change-me"Defina via POST /sessions/:id/webhook:
{ "url": "https://minha.app/webhooks/whatsapp" }Eventos enviados (JSON):
qr:{ event, sessionId, status }authenticated:{ event, sessionId, status }auth_failure:{ event, sessionId, status, message }ready:{ event, sessionId, status }disconnected:{ event, sessionId, status, reason }message:{ event, sessionId, from, to, body, timestamp, type, id }message_ack:{ event, sessionId, id, to, ack }
Timeout e tentativas são controlados por WEBHOOK_TIMEOUT_MS e WEBHOOK_MAX_RETRIES.
- Memória: o processo guarda as sessões ativas em um Map (status, QR em cache, client).
- Disco (
SESSIONS_DIR): LocalAuth e perfil do Chromium por sessão. - Boot: o sistema tenta inicializar cada pasta; se não ficar READY dentro de
BOOTSTRAP_READY_TIMEOUT_MS, mantém os dados e registra aviso (não apaga automaticamente). DELETE /sessions/:id?deleteData=trueremove a pasta da sessão e as variaçõessession-<id>,session-session-<id>, etc.
Em Docker, monte um volume em
/app/sessionspara manter o login entre reinícios.
- Não compartilhe o mesmo
SESSIONS_DIRentre dois processos simultâneos. - Separe pastas de dev e prod (use os profiles do compose).
- Mantenha
HEADLESS=trueno Docker. - Garanta rede estável para o Chromium.
- Trate rate limiting do seu lado.
- Monitore logs de
auth_failureedisconnected.
- 401 Unauthorized: faltou header
x-api-key(ou clique Authorize no Swagger). spawn /usr/bin/chromium ENOENT(Windows): deixeCHROME_EXECUTABLE_PATHvazio; o Puppeteer encontra o navegador.- "The profile appears to be in use": evite rodar duas instâncias na mesma pasta; use um perfil por serviço (prod/dev).
QR not available yet: use?wait=1&timeout=10000nas rotas de QR.Session already exists: já existe pasta/instância com esse id; apague comdeleteData=trueou use outro id.Session not READYao enviar: aguardestatus=READYem/sessions/:id/status.