Gateway de WhatsApp sobre a whatsmeow
Cada cliente do CRM tem o próprio número pareado. O gateway segura todas as sessões no mesmo processo e entrega uma API HTTP igual para todas.
Sem Chrome headless segurando uma aba por conta, sem sessão única que derruba todo mundo quando
cai. É a biblioteca whatsmeow falando o protocolo multi-device direto, num binário
Go que sobe em qualquer lugar.
Ciclo de vida
Uma instância é criada vazia e sobe sozinha até ficar utilizável. Cada estado é consultável em
GET /instances/:id/status, então quem chama nunca precisa adivinhar.
POST /instances reserva a instância e checa o teto por organização antes de alocar qualquer coisa.
GET /instances/:id/qr devolve o código a ser lido no celular. O par é feito uma vez; a sessão persiste no Postgres.
Contatos e grupos entram em segundo plano. GET /sync/status mostra o progresso em vez de deixar a chamada pendurada.
A instância envia, recebe e reage. Se cair, POST /instances/:id/restart reconecta sem novo pareamento.
API
Tudo passa por uma API key no header. As rotas de instância são as mesmas para uma conta ou para cinquenta.
| Rota | Método | Para quê |
|---|---|---|
/instances | POST | Cria uma instância, respeitando o teto da organização. |
/instances/:id/qr | GET | QR de pareamento. |
/instances/:id/status | GET | Estado da conexão, sem adivinhação. |
/messages/text | POST | Envia texto. |
/messages/media | POST | Envia mídia por upload, com o mimetype lido do próprio arquivo. |
/messages/read | POST | Marca como lida. |
/messages/reaction | POST | Reage a uma mensagem. |
/contacts · /groups | GET | O que veio da sincronização, incluindo dados de grupo e foto de perfil. |
/health · /metrics | GET | Health check e métricas para o monitoramento. |
Detalhes que doem
Nenhuma delas é visível num teste com um número só. As três estão no código porque apareceram com tráfego real.
O WhatsApp passou a entregar identificadores @lid em vez do número em @s.whatsapp.net.
O gateway resolve o LID para o número canônico antes de repassar — e, quando não consegue, loga e devolve o original em vez de inventar um contato novo.
Evento de mensagem chega fora de ordem e sem chave estável entre instâncias.
Cada evento recebe um ID snowflake gerado no gateway, então ordenar e deduplicar do lado do CRM não depende de nada que venha de fora.
Rajada de mensagens não pode virar rajada de POST no CRM.
Os eventos passam por um buffer antes de sair, e o webhook vai assinado com HMAC usando CRM_WEBHOOK_SECRET — quem recebe consegue provar a origem.
Rodar
A sessão de cada instância vive no banco, não em disco local — por isso o container pode ser recriado sem pedir QR de novo.
PORT=8080 API_KEY=sua-chave DATABASE_URL=postgres://postgres:postgres@localhost:5432/whatsmeow?sslmode=disable CRM_WEBHOOK_URL=https://sirius.roilabs.com.br/api/webhooks/whatsmeow CRM_WEBHOOK_SECRET=segredo-do-hmac LOG_LEVEL=info # subir docker compose up -dLer o código no GitHub