Introdução à Automação WhatsApp Self-Hosted
No cenário atual de atendimento ao cliente e automação de marketing, a capacidade de gerenciar múltiplos números de WhatsApp simultaneamente é uma necessidade crítica para empresas de médio e grande porte. Enquanto plataformas proprietárias impõem limites rígidos e custos elevados por número ativo, a abordagem self-hosted (hospedagem própria) oferece flexibilidade, controle total sobre os dados e escalabilidade infinita.
O segredo para essa arquitetura reside no uso de bibliotecas que se comunicam diretamente com a API do WhatsApp via protocolo WebSocket, como o Baileys. Ao invés de depender de um único dispositivo físico conectado via USB ou emulação de tela, criamos instâncias isoladas no servidor. Cada instância representa um número único, mantendo seu próprio banco de dados local, sessões e histórico de mensagens.
Neste tutorial, demonstraremos como estruturar uma infraestrutura robusta para rodar múltiplas instâncias do Baileys, integrando-as com plataformas de automação visual como Typebot e sistemas de helpdesk open-source como Chatwoot. Aprenderemos a orquestrar esses containers usando Docker, garantindo que cada número tenha seus recursos isolados para evitar conflitos e garantir alta disponibilidade.
Arquitetura da Solução
Antes de iniciar a instalação, é fundamental entender como os componentes se comunicam. Nossa arquitetura baseia-se em três pilares principais:
- Instâncias Baileys (Backend): Containers leves que mantêm a conexão WebSocket com o WhatsApp Web. Eles não possuem interface gráfica, apenas processam dados brutos.
- Orquestrador/API Gateway: Uma camada intermediária (como um serviço Node.js ou Python) que recebe as requisições externas e as encaminha para a instância correta baseada no número de telefone.
- Frontend/Integração: Ferramentas como Typebot ou Chatwoot que disparam mensagens e recebem eventos via Webhooks.
A vantagem dessa separação é que você pode escalar horizontalmente. Se precisar adicionar mais 50 números, basta iniciar novos containers sem alterar a configuração dos existentes.
Passo 1: Preparando o Ambiente
Para garantir consistência e isolamento, utilizaremos Docker e Docker Compose. Certifique-se de ter o Docker Engine instalado em seu servidor Linux (Ubuntu/Debian recomendados) e permissões de administrador.
Crie um diretório dedicado para o projeto:
mkdir whatsapp-multi-instance
cd whatsapp-multi-instance
Neste diretório, estruturaremos as pastas de dados. Cada instância precisa de seu próprio volume montado para persistir a sessão (QR Code e chaves criptográficas). Crie as seguintes pastas:
mkdir instance-1-data instance-2-data
A estrutura instance-X-data armazenará o arquivo creds.json, essencial para que a instância não precise escanear o QR Code toda vez que reiniciar.
Passo 2: Configurando as Instâncias Baileys
O núcleo da nossa solução é o código JavaScript que utiliza a biblioteca @whiskeysockets/baileys. Como não podemos duplicar o mesmo arquivo de configuração para múltiplas portas dinâmicas, criaremos um script genérico que aceita variáveis de ambiente.
Crie o arquivo server.js na raiz do projeto:
const { makeWASocket, useMultiFileAuthState, DisconnectReason, fetchLatestBaileysVersion } = require('@whiskeysockets/baileys');
const P = require('pino');
const qrcode = require('qrcode');
const fs = require('fs');
const path = require('path');
// Configurações via variáveis de ambiente
const INSTANCE_ID = process.env.INSTANCE_ID || 'default';
const DATA_DIR = process.env.DATA_DIR || `./instance-${INSTANCE_ID}-data`;
const PORT = process.env.PORT || 3000;
// Caminho para a pasta de autenticação
const authState = useMultiFileAuthState(DATA_DIR);
async function startServer() {
console.log(`Iniciando instância ${INSTANCE_ID}...`);
const { version } = await fetchLatestBaileysVersion();
const sock = makeWASocket({
printQRInTerminal: true,
auth: authState,
version,
logger: P({ level: 'silent' }), // Silenciar logs verbosos em produção
browser: ['Ubuntu', 'Chrome', '20.0.04']
});
sock.ev.on('connection.update', async (update) => {
const { connection, lastDisconnect } = update;
if (connection === 'close') {
const shouldReconnect = (lastDisconnect.error as any)?.output?.statusCode !== DisconnectReason.loggedOut;
console.log(`Conexão encerrada. Reconectar? ${shouldReconnect}`);
// Lógica de retry pode ser implementada aqui
} else if (connection === 'open') {
console.log(`Instância ${INSTANCE_ID} conectada com sucesso!`);
}
});
sock.ev.on('creds.update', authState.save);
// Exemplo simples de recebimento de mensagem
sock.ev.on('messages.upsert', async (msg) => {
const message = msg.messages[0];
if (!message.message) return;
console.log(`Mensagem recebida na instância ${INSTANCE_ID}:`, message.message.conversation || message.message.extendedTextMessage?.text);
// Aqui você integraria com seu Webhook ou API externa
// await sendToWebhook(INSTANCE_ID, message);
});
// Servidor HTTP simples para health check e integração
const http = require('http');
const server = http.createServer((req, res) => {
if (req.url === '/health') {
res.writeHead(200);
res.end(JSON.stringify({ status: 'ok', instance: INSTANCE_ID }));
} else {
res.writeHead(404);
res.end('Not Found');
}
});
server.listen(PORT, () => {
console.log(`Servidor rodando na porta ${PORT}`);
});
}
startServer().catch(console.error);
Note que este script cria um servidor HTTP básico apenas para fins de demonstração e health check. Em produção, você provavelmente usará uma biblioteca como Express ou NestJS para lidar com rotas complexas e envios de mensagens.
Passo 3: Definindo o Docker Compose
Agora, definimos a orquestração. O arquivo docker-compose.yml é responsável por iniciar múltiplas cópias do serviço, cada uma com suas variáveis de ambiente e volumes específicos.
version: '3.8'
services:
whatsapp-1:
build: .
container_name: wa-instance-1
environment:
- INSTANCE_ID=1
- DATA_DIR=./data/instance-1-data
- PORT=3001
volumes:
- ./instance-1-data:/app/instance-1-data
restart: unless-stopped
ports:
- "3001:3001"
whatsapp-2:
build: .
container_name: wa-instance-2
environment:
- INSTANCE_ID=2
- DATA_DIR=./data/instance-2-data
- PORT=3002
volumes:
- ./instance-2-data:/app/instance-2-data
restart: unless-stopped
ports:
- "3002:3002"
# Adicione mais serviços seguindo o mesmo padrão para instância-3, etc.
volumes:
data:
Nesta configuração, cada container executa a mesma imagem construída a partir do Dockerfile, mas roda com IDs de instância diferentes. Isso garante que o arquivo de credenciais de um número não sobrescreva o do outro.
Crie também o Dockerfile:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "server.js"]
Passo 4: Integração com Typebot e Chatwoot
Com as instâncias rodando, o próximo passo é conectá-las às ferramentas de automação. O fluxo geral é: Cliente envia mensagem -> WhatsApp Webhook recebe -> Servidor roteia para instância correta -> Typebot processa lógica -> Resposta volta via API da Instância.
4.1 Configurando o Chatwoot
O Chatwoot suporta integrações de canal personalizado (Custom Channel). Para cada número de WhatsApp que você gerencia, deve-se criar um novo canal no Chatwoot:
- Acesse o painel administrativo do Chatwoot.
- Vá em Configurações > Canais.
- Clique em Add Channel e selecione API.
- Crie um canal chamado "WhatsApp Principal" ou "Vendas 1". Anote o
Access Tokengerado.
No seu código de integração (o servidor que conecta o Baileys ao mundo externo), você deve interceptar as mensagens recebidas e enviá-las para a API do Chatwoot usando esse token. O payload JSON deve seguir o formato padrão da API do Chatwoot, incluindo o account_id e o contact_source_id.
4.2 Configurando o Typebot
O Typebot funciona melhor via Webhooks. Ele não se conecta diretamente ao WhatsApp, mas espera receber um evento HTTP quando uma nova mensagem chega.
No seu serviço intermediário (Node.js/Python), configure um endpoint /webhook/typebot:
app.post('/webhook/typebot', async (req, res) => {
const { instanceId, messageText, fromNumber } = req.body;
// Chama o Typebot via HTTP
const response = await axios.post('https://suatypebot.com.br/api/run/ID_DO_FLOW', {
inputs: {
mensagem: messageText,
numero_origem: fromNumber
}
});
// O Typebot retorna a resposta do bot
const botReply = response.data.output;
// Envia de volta para o WhatsApp usando a instância específica
await sendWhatsAppMessage(instanceId, fromNumber, botReply);
res.status(200).send('OK');
});
Desta forma, cada número de telefone aciona seu próprio fluxo no Typebot, mantendo os contextos de conversa separados.
Passo 5: Segurança e Boas Práticas
Ao gerenciar múltiplas instâncias, a segurança torna-se ainda mais crítica. Siga estas diretrizes rigorosamente:
- Firewall (UFW/iptables): Nunca exponha as portas das instâncias (3001, 3002, etc.) diretamente à internet. Elas devem ser acessíveis apenas pelo seu servidor de API ou Gateway.
- Autenticação Webhook: Todos os endpoints que recebem dados do WhatsApp ou enviam para ferramentas externas devem exigir autenticação via Headers (ex:
X-API-Key) ou JWT. - Backup de Credenciais: Os arquivos dentro de
instance-X-datasão vitais. Faça backups automáticos diários desses diretórios. Se perder ocreds.json, perderá o acesso à conta do WhatsApp e precisará escanear um novo QR Code, interrompendo o atendimento. - Limites de Rate: Implemente filas (como BullMQ ou RabbitMQ) para controlar a velocidade de envio. O WhatsApp pune severamente números que enviam muitas mensagens em curto intervalo, podendo banir o número permanentemente.
Passo 6: Monitoramento e Logs
Com dezenas de instâncias rodando, ler os logs do terminal não é viável. Utilize um sistema centralizado de logging como ELK Stack (Elasticsearch, Logstash, Kibana) ou soluções mais leves como Loki com Grafana.
No seu código Node.js, configure o logger para escrever em arquivos JSON rotativos. No Docker Compose, você pode usar volumes compartilhados para coletar esses logs.
Além disso, implemente um serviço de monitoramento de saúde (Health Check). O Docker Compose já faz isso com a diretiva restart, mas é útil ter um painel visual mostrando o status de cada número. Um script simples pode verificar periodicamente o endpoint /health de cada porta e alertar via Telegram ou E-mail se alguma instância cair.
Conclusão
A implementação de múltiplas instâncias de WhatsApp utilizando Baileys, Docker e integrações com Typebot e Chatwoot oferece uma solução poderosa e econômica para automação empresarial. Ao separar as instâncias por containers, você garante estabilidade, facilita o backup e permite escalar conforme a demanda de clientes cresce.
Lembre-se que a manutenção é contínua: atualize as dependências do Node.js regularmente e monitore os logs em busca de erros de desconexão. Com essa infraestrutura sólida, sua empresa estará preparada para atender milhares de conversas simultaneamente sem depender de licenças caras ou softwares de terceiros restritivos.
Para começar, recomenda-se iniciar com duas instâncias de teste em um ambiente de desenvolvimento antes de migrar para a produção. Isso permite ajustar a lógica de roteamento e integrar os webhooks das ferramentas de automação com segurança.