O que você vai ter no final deste manual
Um número de WhatsApp que não é o seu, atendido por um agente que roda na sua VPS.
É isso, sem enfeite. O Claude Code roda na sua máquina como a sua Naia, ganha uma linha própria e passa a aparecer no celular do cliente no lugar de você. Ele lê a mensagem que chega, entende o pedido, olha a sua agenda, oferece os horários que estão livres, marca, registra a conversa e deixa o follow up agendado. Seu celular fica fora disso.
A parte que trava a maioria das pessoas não é a inteligência do agente, é o cano. Ligar um agente num número de WhatsApp parece coisa de engenheiro de integração, e não é. São três passos depois de você ter uma VPS, e o mais técnico deles você não faz na mão: você entrega a documentação para o Claude Code, dá os dados da sua VPS e manda ele instalar e conectar.
Vou te mostrar exatamente como, com o prompt exato para colar, o código que roda de verdade e as coisas chatas que você precisa saber antes de subir.
Pré-requisitos
Junta isso antes de começar:
- Uma VPS Linux, que é onde tudo vai morar. Se você ainda não tem, o Passo 0 mostra como comprar a sua com desconto.
- O Claude Code instalado, rodando como a sua Naia. É ele quem vai fazer o trabalho técnico.
- Um número, que pode ser um chip novo, o seu próprio número ou o WhatsApp Business que você já usa. O Passo 1 é justamente escolher entre os três.
- Uma URL pública para receber o webhook. Pode ser o domínio da sua VPS ou um túnel de teste enquanto você está desenvolvendo.
- Se você for pelo caminho oficial da Meta, uma conta na Zernio e uma conta WhatsApp Business (WABA) dentro de uma conta Meta Business.
Nada aqui exige que você saiba API. Exige que você saiba pedir.
Passo 0 · Você precisa de uma VPS
O agente não mora no seu computador. Ele mora num servidor que fica ligado 24 horas por dia, e isso se chama VPS. É ela que segura a Evolution API, o Claude Code e a sua Naia rodando sem depender de você deixar o notebook aberto.
Compre sua VPS na Hostinger
O que escolher na hora da compra:
- Plano. A recomendada é a KVM8, com 8 núcleos e 32 GB de memória, que aguenta a operação inteira com folga. Se a sua operação é pequena e você está começando, a KVM4 atende.
- Localização do servidor. Escolha Brasil, São Paulo ou Campinas. Servidor perto de você é resposta mais rápida no atendimento.
- Sistema operacional. Ubuntu 22.04. Não escolha outra versão nem outro sistema, porque é essa que o instalador espera.
- Senha do root. Use no mínimo 12 caracteres, misturando maiúscula, minúscula, número e caractere especial. Anote essa senha, você vai precisar dela no Passo 0.1.
- Período do plano. O plano de 24 meses sai bem mais barato por mês. Dá para parcelar em 12 vezes ou pagar no PIX.
Compre por aqui, com o desconto: https://hostinger.com.br/avalanche
Cupom no checkout: AVALANCHE, que dá mais 10% de desconto em cima do valor já promocional.
A Hostinger dá 30 dias de garantia com reembolso de 100%, então se você não gostar, cancela e recebe de volta.
Passo 0.1 · Colete os dados da VPS
Assim que a compra terminar e a VPS estiver ativa, você precisa de três informações. São elas que você vai entregar para o Claude Code no Passo 2.
- Usuário:
root. É sempre root, não tem escolha e não muda. - IP da VPS. Está no painel da Hostinger, no menu VPS, botão Gerenciar. É um número no formato
123.45.67.89. - Senha. É a que você definiu na hora da compra. Se você esqueceu, dá para redefinir ali mesmo no painel, na área de gerenciamento da VPS.
Anota os três num lugar seguro. Sem esses dados, ninguém entra na máquina, nem você, nem o Claude.
Passo 1 · Escolhe o número
Antes de qualquer linha de código, decide qual linha vai ser a do agente. São três caminhos, e cada um tem um preço que não é em dinheiro.
Caminho A · Chip novo, conectado pela Evolution
Você compra um chip qualquer, de qualquer operadora, o Claude instala a Evolution na sua VPS, sobe a instância, aparece um QR code, você lê com o WhatsApp daquele chip e pronto. Em poucos minutos está conectado.
A favor: é o mais rápido, não tem fila de aprovação, não depende de verificação da Meta e o número nasce limpo, só para o agente. É o caminho que eu recomendo para quem está começando hoje.
Contra: é conexão não oficial, você não tem contrato com a Meta e o risco de bloqueio é seu. Número novo precisa ser aquecido com calma, e isso está explicado lá embaixo.
Caminho B · O seu próprio número
O número que já está no seu celular, conectado pela Evolution do mesmo jeito.
A favor: custo zero, você testa hoje e sente o fluxo funcionando antes de gastar com chip.
Contra: é o seu número pessoal ou comercial recebendo automação. Serve para testar, não serve para operação. Na hora que virar rotina, migra para um chip só do agente.
Caminho C · WhatsApp oficial da Meta, pela Zernio
Esse é o caminho oficial, e ele ficou bem mais simples do que era. A Zernio é uma API REST única para o WhatsApp Business: você conecta o número pelo Embedded Signup da Meta e passa a falar com uma chave de API só, sem navegar no Business Manager e sem processo de App Review.
O que a documentação dela diz, em resumo, e vale conferir na fonte antes de montar sua operação em cima:
- Você precisa de uma conta WhatsApp Business (WABA) dentro de uma conta Meta Business. A conexão acontece pelo Embedded Signup ou por um token de System User da Meta.
- Existe um modo de coexistência, em que o aplicativo do WhatsApp Business continua com o número. Nesse modo, a API de grupos não fica disponível.
- É um número por perfil. A conexão começa por uma chamada em
GET /v1/connect/whatsapp, com oprofileIde aredirect_url. - Conversa sai por
POST /v1/inbox/conversationse disparo em lista sai porPOST /v1/broadcasts, com agendamento e variáveis por destinatário. - Dá para mandar texto, imagem, vídeo, documento, áudio, localização, cartão de contato, mensagens interativas, templates e Flows, que são os formulários, pesquisas e agendamentos dentro do chat.
- Fora da janela de 24 horas de atendimento, a conversa só sai com template.
- Tem webhook de entrega, com os eventos
message.deliveredemessage.read, além de chamada de voz de entrada e de saída e atribuição de anúncio click to WhatsApp. - A Meta cobra a taxa de conversa dela direto na sua WABA, na tarifa dela, sem nada por cima.
A favor: é oficial, tem regra escrita, nome de exibição aprovado, verificação e muito menos risco de bloqueio. Libera botão, lista e formulário dentro da conversa, que a conexão por QR code não tem. E é bem mais fácil do que montar um CRM inteiro só para ter o número oficial.
Contra: tem processo de verificação, exige que você respeite a janela de 24 horas e os templates aprovados, e não sobe em dois minutos.
Regra prática que eu uso: começa com o que você já tem, prova que o fluxo funciona e só depois decide onde a operação vai morar de verdade. Não precisa migrar nada para começar.
Passo 2 · Manda o Claude instalar a Evolution e gerar o QR code
Aqui é onde a maioria desiste, porque acha que vai ter que aprender Docker, banco de dados e API de uma vez. Não vai.
Você abre o Claude Code, cola o prompt abaixo trocando só o que está entre chaves, e assiste. Ele entra na sua VPS por SSH, instala o Docker, sobe a Evolution API v2 com Postgres e Redis, cria a instância, gera o QR code, configura o webhook e testa o primeiro envio. A documentação oficial da Evolution está em https://doc.evolution-api.com e o próprio Claude lê ela antes de escrever qualquer comando.
O prompt, para colar inteiro no Claude Code
Você vai instalar a Evolution API v2 na minha VPS e conectar o meu WhatsApp.
Dados da VPS:
IP: {IP_DA_SUA_VPS}
usuário: root
senha: {SENHA_DA_SUA_VPS}
Documentação oficial: https://doc.evolution-api.com
Leia a documentação da versão 2 antes de escrever qualquer comando e não
invente nome de variável, de endpoint nem de imagem: confira tudo na doc.
Faça nesta ordem, um passo de cada vez, me mostrando a saída real de cada comando:
1. Entre na VPS por SSH com esses dados. Confirme a versão do Ubuntu, o espaço
em disco e a memória livre antes de instalar qualquer coisa.
2. Instale o Docker e o plugin do docker compose pelo repositório oficial do
Docker. Confirme com docker --version e docker compose version.
3. Crie a pasta /opt/evolution e escreva ali um docker-compose.yml com três
serviços, seguindo o modelo da documentação oficial da v2:
um serviço da Evolution API v2, publicando a porta 8080, com volume nomeado
para os dados da instância e restart always;
um serviço de Postgres, com volume nomeado e um banco chamado evolution;
um serviço de Redis, com volume nomeado, para o cache.
4. Gere a AUTHENTICATION_API_KEY e a senha do Postgres com openssl rand -hex 32.
Nenhuma senha escrita à mão e nenhum segredo dentro do docker-compose.yml:
tudo vai num arquivo .env ao lado do compose, com permissão 600, e o compose
só referencia as variáveis. Configure no .env, no mínimo, a chave de
autenticação, o banco de dados ligado com o provedor e a URI de conexão do
Postgres, o cache no Redis e a URL pública do servidor, usando exatamente os
nomes de variável que estiverem na documentação da v2.
5. Suba com docker compose up -d. Acompanhe docker compose logs -f até parar de
subir erro e me mostre o retorno de curl na raiz da API e no endpoint de
status, com o header apikey.
6. Só depois que a chave de autenticação estiver configurada, libere a porta no
firewall e me diga qual é o endereço de acesso da API.
7. Crie a instância chamada "naia" pelo endpoint de criação de instância da v2,
com qrcode ligado e a integração de WhatsApp por QR code. Salve o token que a
resposta devolve para essa instância no .env, separado da chave global, e me
explique em uma linha qual é a diferença entre as duas chaves.
8. Pegue o QR code que voltou na resposta, decodifique o base64 e salve como
/opt/evolution/qrcode.png. Me avise quando o arquivo estiver pronto e me diga
como eu faço para visualizar ou baixar esse arquivo.
9. Enquanto eu leio o QR code com o celular, consulte o estado da conexão da
instância a cada 5 segundos, por até 2 minutos, e me avise no terminal na hora
em que o estado virar open.
10. Configure o webhook da instância apontando para
{SUA_URL_PUBLICA}/webhook/whatsapp com os eventos MESSAGES_UPSERT,
CONNECTION_UPDATE e QRCODE_UPDATED, e me mostre a resposta.
11. Envie uma mensagem de teste para {SEU_NUMERO} pelo endpoint de envio de texto
e me mostre a resposta crua da API.
Regras: nada de SDK de terceiro, use curl e fetch nativo. Nunca escreva senha,
chave ou token dentro de código, de script ou de log, só no .env com permissão 600.
Se algum passo devolver erro, pare, me mostre o comando, o status e o corpo da
resposta, e não tente adivinhar o que deu errado nem pular etapa.Enquanto ele trabalha, você deixa o celular do número escolhido na mão. Quando ele avisar que o qrcode.png está pronto, você abre a imagem, aponta a câmera do WhatsApp e lê. O estado da conexão vira open e a linha passa a ser do agente.
Uma observação que economiza sua cabeça mais tarde. São duas chaves diferentes: a chave global do servidor, que cria, conecta e apaga instância, e o token da instância, que volta na criação e é o que você usa no dia a dia para enviar. Guarda as duas separadas e nunca comita nenhuma delas.
Se você já tem a Evolution rodando
Pula a instalação e manda só a parte de conectar. O prompt encolhe para isto:
Minha Evolution API v2 já está rodando. A URL e a chave global estão no .env,
em EVO_URL e EVO_APIKEY, e a documentação oficial é https://doc.evolution-api.com.
Crie a instância "naia" com qrcode ligado, salve o token dela no .env como
EVO_TOKEN, decodifique o qrcode e salve como qrcode.png, configure o webhook em
{SUA_URL_PUBLICA}/webhook/whatsapp com MESSAGES_UPSERT, CONNECTION_UPDATE e
QRCODE_UPDATED, acompanhe o estado da conexão até virar open e envie uma mensagem
de teste para {SEU_NUMERO}. Me mostre a resposta crua de cada chamada.Se você já usa um CRM
Você não precisa trocar de ferramenta e não precisa entender a API dele. Baixa a documentação do CRM que você já usa, joga dentro da pasta do projeto e manda o Claude Code ler:
A documentação da API do CRM que eu uso está em docs/crm/. Leia ela inteira e
me explique, em português e em passos numerados, como conectar esse CRM na minha
Evolution API v2, que já está rodando nesta VPS.
Depois de explicar, implemente: quando chegar mensagem nova no webhook da
Evolution, crie ou atualize o contato no CRM; quando o agente marcar um horário,
crie o compromisso na agenda do CRM; e registre no CRM a conversa inteira.
Use só endpoints que existam na documentação que eu te dei, sem inventar rota.
Se faltar alguma informação na doc, pare e me pergunte em vez de chutar.Funciona com o CRM que você já paga, porque quem lê a documentação é ele, não você.
Passo 3 · O envio
Não existe SDK mágico. Mandar mensagem é uma chamada HTTP com a apikey no cabeçalho e número mais texto no corpo.
// enviar.js
const BASE = process.env.EVO_URL; // sua Evolution, na sua VPS
const TOKEN = process.env.EVO_TOKEN; // o token da instância
const INSTANCIA = "naia";
export async function enviarTexto(numero, texto) {
const resp = await fetch(`${BASE}/message/sendText/${INSTANCIA}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
apikey: TOKEN
},
body: JSON.stringify({
number: numero, // 55 + DDD + número, só dígitos
text: texto
})
});
const data = await resp.json();
if (!resp.ok) {
console.error("[envio] falhou", resp.status, JSON.stringify(data));
return null;
}
console.log("[envio] ok", resp.status, data?.key?.id);
return data?.key?.id; // guarde esse id, é o rastro da mensagem
}
await enviarTexto("5511999999999", "Oi! Aqui é a Naia. Posso te ajudar?");A mesma coisa em curl, para você testar antes de escrever qualquer aplicação:
curl -X POST "$EVO_URL/message/sendText/naia" \
-H "apikey: $EVO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"number":"5511999999999","text":"Oi! Aqui é a Naia. Posso te ajudar?"}'Dois detalhes que derrubam o envio de gente experiente. O campo text fica na raiz do corpo, não dentro de um objeto de mensagem. E o número vai com o código do país junto, só dígitos, sem +, sem parênteses e sem traço. No Brasil isso quer dizer 55 mais DDD mais o número.
Receber é a outra ponta
Você não fica perguntando para a Evolution se chegou mensagem. Ela avisa você. O evento MESSAGES_UPSERT bate na rota que você configurou no Passo 2, com este formato, resumido no que importa:
{
"event": "messages.upsert",
"instance": "naia",
"data": {
"key": {
"remoteJid": "5511999999999@s.whatsapp.net",
"fromMe": false,
"id": "3EB0C767D26B8F1C2A44"
},
"pushName": "Marina",
"message": { "conversation": "Oi, vocês têm horário na sexta?" },
"messageTimestamp": 1789776127
}
}E o receptor, inteiro:
// webhook.js
app.post("/webhook/whatsapp", async (req, res) => {
res.sendStatus(200); // responde primeiro, processa depois
const body = req.body || {};
if ((body.event || "").toLowerCase() !== "messages.upsert") return;
const data = body.data || {};
const key = data.key || {};
if (key.fromMe) return; // eco do que você mandou
if (String(key.remoteJid).endsWith("@g.us")) return; // grupo, ignora
const numero = String(key.remoteJid).split("@")[0].split(":")[0];
const texto = data.message?.conversation
|| data.message?.extendedTextMessage?.text
|| "";
if (!texto) return; // áudio, imagem, figurinha
const resposta = await pedirParaONaia(texto, numero); // aqui entra o seu agente
await enviarTexto(numero, resposta);
});Acabou. Esse é o setup inteiro. O resto do trabalho é a conversa, e a conversa é com o Claude.
Responder 200 antes de processar é o ponto mais importante desse bloco. Se você deixar a Evolution esperando o agente pensar, ela vai considerar a entrega falha e reenviar o mesmo evento, e o cliente recebe resposta duplicada.
O resultado
Na prática, é assim que aparece na tela do cliente, no WhatsApp dele, igual a qualquer outra conversa:
Cliente Oi, vocês têm horário na sexta? 18:40
Naia Tenho sim. Sexta tenho 16h e 18h30 livres. 18:40 lido
Qual fica melhor pra você?
Cliente 18h30 18:41
Naia Fechado, marquei sexta às 18h30. 18:41 lido
Já está na agenda e eu te lembro na quinta.O que acontece por baixo, em ordem: a mensagem chega pelo webhook, o agente entende o pedido, consulta a sua agenda, vê o que está livre, oferece as opções, recebe a escolha, cria o evento no calendário e deixa o lembrete agendado. O contato, a conversa inteira e o compromisso ficam registrados no mesmo lugar, então quando você abrir o painel na segunda-feira, a história está toda lá.
Seu celular não tocou nenhuma vez.
Bônus · resolver tudo dentro do chat
O cliente não precisa sair da conversa para fechar nada.
No WhatsApp oficial da Meta, pela Zernio, você tem mensagem interativa, que é o botão de resposta rápida e a lista de opções, e tem Flow, que é o formulário dentro do chat para pesquisa, cadastro e agendamento, com a pessoa escolhendo serviço e horário no toque. Também vai foto, vídeo, documento, áudio, localização e cartão de contato dentro do próprio balão, e template para quando a janela de 24 horas já fechou. Tudo sem abrir página e sem instalar aplicativo.
Na conexão por QR code, esses componentes não existem, e a solução é mais simples do que parece: menu numerado. O agente manda as opções de 1 a 3, o cliente responde "2" e o agente entende. Funciona, converte e não depende de aprovação de ninguém.
Muda o formato do botão, não muda o resultado.
Para quem constrói
A diferença entre um brinquedo e uma operação que aguenta cliente real é enxergar o que está passando. Registra tudo, desde o primeiro dia:
- Toda mensagem que entra: o
remoteJid, opushName, o texto e omessageTimestamp. - Todo envio que sai: o status HTTP, o
key.idda resposta e o número de destino. - Todo webhook que chega: o evento e o corpo cru, pelo menos nos primeiros dias.
- Toda ação na agenda: qual evento foi criado, para quem e em que horário.
Um painel de log honesto se parece com isto:
18:42:07 POST /message/sendText/naia 200 enviado
18:42:09 HOOK MESSAGES_UPSERT 200 recebido
18:42:10 POST /message/sendText/naia 200 resposta do agente
18:42:11 CRM agenda.criar_evento 201 sexta 18h30
18:43:55 POST /message/sendText/naia 401 apikey inválida
18:44:02 POST /message/sendText/naia 200 reenviadoOs status que você vai encontrar e o que cada um quer dizer:
- 200 no envio: a Evolution aceitou e devolveu o
key.id. Guarda esse id, é ele que amarra o envio ao que aparece depois noMESSAGES_UPDATE. - 401: apikey inválida. Quase sempre é a chave trocada, o token global no lugar do token da instância ou o contrário.
- 404: a instância não existe com esse nome. Confere a grafia na URL.
- 400: corpo errado. Normalmente o
textfoi aninhado em algum objeto ou o número foi sem o código do país. connectionStateemclose: a sessão caiu, a linha está desconectada. Gera o QR code de novo e lê com o celular.connecting: está subindo, ainda não terminou o pareamento. Espera antes de tentar enviar.
Quando alguma coisa travar, você abre o log e vê onde parou, em vez de adivinhar. Depurar é a parte que a maioria dos setups de WhatsApp faz mal.
Leia duas vezes
Isso aqui não é burocracia, é o que protege o seu número e o seu nome.
Dois caminhos, regras diferentes. O WhatsApp oficial da Meta, pela Zernio, tem verificação, nome de exibição aprovado e regra escrita sobre o que você pode mandar. A conexão por QR code sobe mais rápido e não tem contrato com a Meta, então o risco de bloqueio é inteiramente seu.
Janela de 24 horas, no oficial. Passou 24 horas desde a última mensagem que o cliente te mandou, você só volta a falar com um template aprovado antes. Fora disso, a mensagem não sai. Isso está escrito na documentação da própria Zernio.
Opt in sempre, nos dois caminhos. Você fala com quem pediu para falar com você. Lista comprada queima o número e queima a sua reputação junto, e não tem conexão que salve isso.
Número novo se aquece devagar. No oficial, a documentação da Zernio avisa que um número recém conectado começa no nível mais baixo de envio da Meta e sobe conforme o uso e a qualidade. No QR code não existe nível nenhum, e por isso a disciplina precisa vir de você: os primeiros dias são de volume baixo e conversa de verdade, gente respondendo de volta, nunca disparo em massa. Número que nasce metralhando morre na primeira semana.
Preço, cota e limite mudam, e não são nossos. A Meta cobra a taxa de conversa dela direto na sua WABA, na tarifa dela. Não conta com nenhum número que você leu em post de internet, inclusive este manual. Confere na fonte oficial antes de montar sua operação em cima de qualquer promessa.
Checklist final
- VPS comprada, ativa e com Ubuntu 22.04
- IP, usuário
roote senha da VPS anotados e à mão - Claude Code rodando como a sua Naia
- Número escolhido entre os três caminhos, e o motivo da escolha claro para você
- Evolution API v2 instalada pelo Claude na VPS, com Postgres e Redis no ar
- Chave de autenticação e senhas geradas, guardadas no
.envcom permissão 600, e nada de credencial dentro do código - Instância criada, QR code lido e estado da conexão devolvendo
open - Webhook configurado com
MESSAGES_UPSERTe respondendo200antes de processar - Primeiro envio de teste com
200ekey.idno log - Primeira mensagem recebida aparecendo no seu log com o texto certo
- Agenda conectada ao agente, ou a documentação do seu CRM entregue ao Claude Code
- Log de entrada, saída e erro escrevendo em arquivo ou banco
- Opt in, janela de 24 horas e aquecimento do número entendidos e respeitados
Próximo passo
Se você chegou até aqui e o número está respondendo, você já fez o que a maioria só assiste alguém fazer no vídeo.
O passo seguinte é parar de tratar isso como um experimento e montar a sua Naia de verdade, com memória, agenda, follow up e o time inteiro trabalhando por você.
Na Imersão eu mostro isso ao vivo, do zero, no mesmo formato que eu uso aqui dentro: https://imersao.denderson.ai
E se você ainda não tem a sua, comece pela Primeira Naia, que é a instalação guiada da sua primeira agente na sua própria VPS: https://primeira-naiav2.denderson.ai
Um número. Uma tarde. Um agente trabalhando 24 horas por dia.
@denderson.ai · Avalanche