Um agente atende no WhatsApp, qualifica e passa o lead para o colaborador livre há mais tempo, pelo Telegram, com tudo o que ele precisa para ligar.
A instalação no servidor pede um técnico: veja Deploy na VPS.

O atende-leads é um sistema de atendimento de leads (clientes em potencial) pelo WhatsApp, feito para pequenos negócios como loja, escola, imobiliária ou prestador de serviço. Um agente responde às dúvidas, pergunta o que a pessoa procura e dá uma nota ao lead: quente, morno ou frio. Quando ela pede uma pessoa ou mostra que quer comprar, o sistema avisa pelo Telegram o colaborador que está livre há mais tempo, com os dados e um botão para ligar. Para usar, você precisa de um servidor (VPS) com Docker, um domínio, um número de WhatsApp só para o atendimento e um bot do Telegram.
O agente cuida da primeira conversa. Quando há chance de venda ou alguém precisa de ajuda, o lead vai para uma pessoa da equipe, na vez certa, e ninguém fica esquecido na fila.
Responde o FAQ, mostra preço só do que está cadastrado, pergunta nome, o que procura e para quando, e dá a nota do lead (quente, morno ou frio). Funciona pela Evolution API.
Em 3 casos: o lead pede, o agente percebe que precisa (reclamação, problema, dois "não entendi") ou vê chance de venda (preço, "quero comprar", pagamento).
Vai ao colaborador livre há mais tempo, no privado: nome, telefone, nota, interesse, resumo e últimas mensagens, com os botões ✅ Assumir · ↪️ Passar · 📞 Ligar · 💬 WhatsApp.
Sem resposta em 2 min, a oferta passa ao próximo. Lead quente sem dono em 2 min (normal em 5) ou ninguém livre: alerta no grupo, e o primeiro que tocar em Assumir leva.
Uma mensagem fixada no grupo, EQUIPE AGORA, mostra quem está livre, atendendo, ocupado ou fora, e a fila de leads esperando. É editada a cada mudança.
Vendeu (valor) · Retornar (data) · Negociando · Sem interesse (motivo) · Inválido, com nota em texto ou áudio (o áudio vira texto só com WHISPER_CMD configurado no .env). O colaborador volta ao fim da fila de livres.
Lead: chat, bolha para o site e página "meu atendimento". Equipe: /equipe. Gestor: /gestor, com ao vivo, hoje, leads, cadastros e exportação CSV.
O lead pode mandar PARAR ou apagar meus dados. O grupo da equipe nunca recebe telefone nem texto do lead: só primeiro nome, motivo, nota e tempos.
Por padrão o agente funciona por regras, sem custo e sem API. Opcional: Ollama local ou um endpoint compatível com OpenAI do próprio negócio, com volta às regras se falhar.
Cada lead segue o mesmo caminho. O sistema guarda tudo num banco SQLite no seu servidor.
Pediu humano ("atendente", "me liga"), chance de venda ("quanto custa", "quero comprar", "orçamento") ou precisa de atenção ("problema", "reclamação", "cancelar").
Os colaboradores livres ficam em fila pelo horário em que ficaram livres. Quem fecha um atendimento volta ao fim da fila. Lead que volta em até 30 dias é oferecido primeiro a quem o atendeu.
Oferta: 2 min. Alerta: 2 min para lead quente, 5 min para normal. Lembrete de atendimento parado: 30 min. Tudo ajustável no cadastro.
Quem faz cada passo:
O script pergunta nome, domínio e Telegram; gera segredos, o .env e o dados/negocio.json; sobe o Docker. No fim mostra o token do gestor: guarde, é a senha do /gestor. Detalhes em Deploy na VPS.
./instalar-vps.sh
Abra um túnel até a VPS, entre no painel da Evolution em http://localhost:8081/manager, crie a instância e leia o QR Code com o número do atendimento. Use um número só para isso.
ssh -L 8081:127.0.0.1:8081 usuario@vps
Em /gestor → Cadastros: equipe (nome, telefone, código), produtos com palavras e preço, FAQ, horários e prazos.
Cada colaborador manda /entrar <código> no privado do bot e toca 🟢 Livre. O código de cada um é o que o instalador imprimiu (no teste local, o do negocio.json, como ana-1234).
/entrar <código>
Pelo WhatsApp ou pela bolha de chat do site. Para pôr a bolha no seu site, cole esta linha antes do </body> das páginas:
<script src="https://<domínio>/widget.js" async></script>
Responde o FAQ, informa o preço cadastrado, qualifica e dá a nota.
Pediu humano, precisa de atenção ou chance de venda: abre o chamado e manda uma linha curta ao grupo, sem telefone.
Vai ao livre há mais tempo, no privado, com dados e botões. 2 min sem resposta: passa ao próximo.
Mensagem no grupo quando estoura o prazo ou não há ninguém livre. O primeiro que tocar em Assumir leva.
Liga (📞), responde pelo WhatsApp (💬) ou responde no próprio bot, respondendo (reply) ao cartão do atendimento.
O colaborador escolhe o resultado; o sistema grava o histórico, agenda o retorno e devolve o colaborador à fila.
Em /gestor: ao vivo, hoje, leads e exportar CSV.
Uma cópia do banco e do cadastro por dia, pelo cron único que o instalar-vps.sh instala, guardada em /opt/atende-leads-backups/.
docker compose exec -T atende ./atende backup ...
Para testar no seu computador basta o Python 3. Para usar de verdade, com WhatsApp e Telegram, você precisa do resto.
Só a biblioteca padrão: nada de pip install para rodar. Para rodar os testes, o pytest.
python3 --versionUm servidor Linux com Docker e Docker Compose. O app escuta só em 127.0.0.1:8080.
docker compose versionUm proxy reverso (Caddy ou Nginx) do seu domínio para 127.0.0.1:8080. Os webhooks precisam de HTTPS.
Um número só para o atendimento, conectado pela Evolution API. Ela pode subir no mesmo compose (perfil evolution).
Crie o bot no @BotFather e ponha no grupo da equipe como administrador, para ele fixar o painel.
Cada colaborador com Telegram e um código pessoal, que serve para o /entrar e para o painel /equipe.
dados/leads.db), sem servidor à parte.docker-compose.yml que só sobe quando pedido, como --profile evolution./gestor; nas chamadas à API vai no cabeçalho X-Token.Sem WhatsApp e sem Telegram configurados, as integrações ficam desligadas e os painéis web funcionam. É o jeito mais rápido de ver o sistema.
Clone o repositório e entre na pasta.
git clone https://github.com/inematds/atende-leads.git cd atende-leads
Na primeira vez, o sistema copia o exemplos/negocio.json para a pasta de dados. Se a porta 8080 estiver ocupada no seu computador, use outra (por exemplo --porta 8090) e troque nos endereços dos passos seguintes.
./atende serve --porta 8080 --dados dados
Chat do lead em http://localhost:8080/, equipe em /equipe (entra com o código do colaborador) e gestor em /gestor (entra com o token_gestor do dados/negocio.json).
# no exemplo, o token do gestor é TROQUE-ESTE-TOKEN e o código da Ana é ana-1234 grep -E '"token_gestor"|"codigo"' dados/negocio.json
Pelo /gestor → Cadastros, ou editando o dados/negocio.json. O agente só fala preço que estiver aqui. O arquivo vem com dados de exemplo (a colaboradora Ana, o "Plano anual", um FAQ de loja): troque pelos seus antes de receber leads de verdade.
{
"nome": "Loja Exemplo",
"equipe": [ { "id": "ana", "nome": "Ana", "telefone": "5541999990010", "codigo": "ana-1234" } ],
"produtos": [ { "id": "plano-anual", "nome": "Plano anual", "palavras": ["anual", "plano anual"], "preco": "R$ 1.200" } ],
"prazos": { "oferta_min": 2, "alerta_quente_min": 2, "alerta_normal_min": 5, "lembrete_min": 30 },
"cerebro": { "tipo": "regras" }
}
Com ATENDE_AGORA o "agora" fica fixo, e você roda as tarefas de tempo (ofertas vencidas, alertas, lembretes) quando quiser. Pare antes o servidor do passo 2 (Ctrl+C) e rode o curl num segundo terminal, porque o serve ocupa o primeiro.
ATENDE_AGORA=2026-11-09T10:00 ./atende serve --porta 8080 --dados dados curl -X POST -H "X-Token: TROQUE-ESTE-TOKEN" http://localhost:8080/api/tarefas/rodar
São 320 testes de contrato. O build e o container Docker só entram com TESTAR_DOCKER=1.
python3 -m pytest -q tests/
/entrar <código> liga o seu Telegram ao seu cadastro/livre · /ocupado · /fora mudam o seu status/meus seus atendimentos · /retornos retornos de hoje/lead <nome ou telefone> ficha curta/fechar <id> vendeu <valor> (ou retornar, negociando, sem, invalido)/status manda o painel EQUIPE AGORA (só no grupo da equipe)Variáveis em .env.exemplo. Sem as variáveis de uma integração, ela fica desligada e o resto funciona (painéis web).
Antes de instalar: crie o bot no @BotFather e ponha no grupo da equipe. O instalador pede o token do bot e o ID do grupo (número que começa com -100). Para achar o ID, mande no grupo um comando para o bot, como /start@nome_do_bot (no modo de privacidade padrão, mensagem comum não chega ao bot), e abra o endereço abaixo; o chat.id do grupo aparece na resposta. Faça isso antes de registrar o webhook (passo 6), porque depois o getUpdates deixa de responder.
curl "https://api.telegram.org/bot<TOKEN>/getUpdates"
Na VPS, como root (ou com sudo), com git, python3 e Docker instalados, clone o projeto e rode o instalador. Ele gera o .env e o dados/negocio.json só para o dono (umask 077), o token do gestor e os códigos da equipe (aleatórios), passa a pasta dados/ ao uid 1000 (o container não roda como root; sem ser root, tenta sudo -n chown e, se não conseguir, para antes de subir e mostra o comando), sobe o compose, com ou sem a Evolution, instala o cron do backup e imprime tudo no fim: guarde. Rodar de novo (mudar o domínio, ligar o Telegram) não troca os segredos: os que já estão no .env são reaproveitados e a saída diz quais; só os que faltam são gerados (uma senha nova do Postgres derrubaria a Evolution, e um TELEGRAM_SEGREDO novo faria o webhook do Telegram dar 401). O cadastro parte do exemplo (a colaboradora Ana, o "Plano anual", um FAQ de loja): troque pelos seus em /gestor → Cadastros antes de receber leads. A aplicação escuta só em 127.0.0.1:8080 e a Evolution em 127.0.0.1:8081.
git clone https://github.com/inematds/atende-leads.git /opt/atende-leads cd /opt/atende-leads ./instalar-vps.sh # sem o instalador: cp .env.exemplo .env, troque todos os segredos do .env # e o token_gestor e os códigos em dados/negocio.json (12+ caracteres); # com a Evolution junto, ponha EVOLUTION_URL=http://evolution-api:8080 no .env; sudo chown -R 1000:1000 /opt/atende-leads/dados # o container roda como uid 1000 docker compose up -d --build # ou, com a Evolution junto: docker compose --profile evolution up -d --build
Se o negocio.json ainda tem token_gestor = TROQUE-ESTE-TOKEN ou algum código igual ao do exemplos/negocio.json, ou se uma integração está ligada com WEBHOOK_SEGREDO/TELEGRAM_SEGREDO vazio, com menos de 16 caracteres ou contendo troque (ou com EVOLUTION_API_KEY contendo troque), o servidor só aceita subir em 127.0.0.1/localhost. Em 0.0.0.0 (como no Docker) ele recusa com a mensagem "rode ./instalar-vps.sh ou troque os segredos". Só para teste ou demonstração existe ATENDE_PERMITIR_EXEMPLO=1 (nunca em produção).
Proxy reverso (Caddy ou Nginx) do seu domínio para 127.0.0.1:8080. O instalador já preenche url_publica no negocio.json com o domínio; confira. Os limites por IP (5 senhas erradas por minuto: as erradas recebem 429, a certa sempre passa; 5 conversas novas do chat web em 10 min, além de um teto no total; 3 pedidos de atendimento pelo web por hora) usam o IP do X-Forwarded-For só quando a conexão vem de um proxy confiável: ATENDE_PROXY_CONFIAVEL (CIDRs; o padrão cobre o loopback e as redes privadas, por onde o Caddy no host chega ao container). Vazio = nunca confiar no X-Forwarded-For. Quem assume um lead é só o colaborador a quem ele foi ofertado ou, em alerta 🚨, quem tocar primeiro em Assumir.
# Caddyfile
leads.seudominio.com { reverse_proxy 127.0.0.1:8080 }
A porta da Evolution só escuta em 127.0.0.1, então abra um túnel do seu computador e, com ele aberto, acesse http://localhost:8081/manager. Entre com a API Key global (EVOLUTION_API_KEY do .env), clique em Instance +, dê o nome de EVOLUTION_INSTANCIA (padrão loja), canal Baileys, salve, abra a instância e leia o QR Code pelo WhatsApp do celular (Aparelhos conectados → Conectar aparelho).
ssh -L 8081:127.0.0.1:8081 usuario@vps # sem o manager, pela API, na VPS: curl -X POST http://127.0.0.1:8081/instance/create -H "apikey: <EVOLUTION_API_KEY>" \ -H 'Content-Type: application/json' -d '{"instanceName":"loja","integration":"WHATSAPP-BAILEYS","qrcode":true}'
Evento MESSAGES_UPSERT para o endereço abaixo. Com o perfil evolution, o compose já aponta pela rede interna; com uma Evolution em outro servidor, preencha EVOLUTION_URL no .env e configure o webhook nela. Mensagens com remoteJid @lid usam o telefone de remoteJidAlt/senderPn; sem ele, aparecem em descartes no /gestor.
https://<domínio>/webhook/evolution/<WEBHOOK_SEGREDO>
Com o bot no grupo da equipe como administrador (para fixar o painel), registre o webhook. O token e o TELEGRAM_SEGREDO estão no .env gerado pelo instalador (grep TELEGRAM_ .env).
curl "https://api.telegram.org/bot<TOKEN>/setWebhook?url=https://<domínio>/webhook/telegram&secret_token=<TELEGRAM_SEGREDO>"
O instalar-vps.sh já instala no crontab do root. Roda dentro do container e copia para fora do volume de dados, numa pasta 700 com arquivos 600 (o backup tem dados pessoais), guardando 30 dias.
0 3 * * * cd /opt/atende-leads && docker compose exec -T atende ./atende backup --dados /dados --saida /dados/.backup-cron.tar.gz && install -d -m 700 /opt/atende-leads-backups && f=/opt/atende-leads-backups/leads-$(date +\%F).tar.gz && mv /opt/atende-leads/dados/.backup-cron.tar.gz $f && chmod 600 $f && find /opt/atende-leads-backups -name 'leads-*.tar.gz' -mtime +30 -delete # atende-leads-backup
Pare o serviço antes: o restaurar recusa com o servidor no ar.
docker compose stop atende docker compose run --rm -v /opt/atende-leads-backups:/backups atende \ ./atende restaurar --dados /dados --arquivo /backups/leads-AAAA-MM-DD.tar.gz docker compose start atende
Envios ao Telegram e ao WhatsApp saem por uma caixa de saída em segundo plano: se uma API travar, o atendimento e o /api/saude continuam respondendo. Acima de 64 conexões ao mesmo tempo (ATENDE_MAX_CONEXOES) o servidor responde 503, mas o /api/saude responde mesmo lotado. Para atualizar: git pull && sudo chown -R 1000:1000 dados && docker compose up -d --build.
dados/); o backup contém dados pessoais, guarde com acesso restrito. A Evolution do compose não guarda cópia das mensagens nem dos contatos (LGPD). Quem tem o token do gestor pode mudar o endereço do cérebro no /gestor: por isso a CEREBRO_API_KEY só vai a um host listado em CEREBRO_HOSTS_PERMITIDOS no .env (vazio = a chave não é enviada).Telas reais do sistema rodando com dados de exemplo (uma escola de idiomas fictícia, segunda-feira 10h). As do Telegram foram montadas com os textos exatos que o sistema enviou.



EQUIPE AGORA no grupo: status de cada um e a fila.


/equipe: status, ofertas e alertas, atendimentos abertos e retornos de hoje.

/gestor ao vivo: equipe, fila com alertas e quem está atendendo, com transferência.
/gestor hoje: leads novos, chamados, vendas, valor vendido e números por colaborador.A versão 1.11.8 faz o que está descrito acima. Estas ideias ficaram para depois.