Primeiros passos
No primeiro pareamento, use um número de WhatsApp descartável (chip de teste), não o número que você usa de verdade nem o de um cliente. Ver Introdução para o motivo: o risco de banimento é real, e o primeiro teste é justamente onde mais coisa pode dar errado.
Existem dois caminhos para começar: pelo portal (recomendado, sem escrever código) ou direto pela API (para integrar com seu próprio sistema). Os dois fazem a mesma coisa por baixo: criar uma instância, parear por QR Code e enviar uma mensagem de teste.
Caminho 1 — pelo portal (recomendado)
- Acesse
/panelno host onde a API está rodando e entre com um usuário do portal (o administrador inicial é semeado a partir deADMIN_EMAIL/ADMIN_PASSWORD, configurados no ambiente). - Na tela Instâncias, preencha o nome da instância (ex.:
minha-instancia) e clique em Criar instância. - Clique em Conectar na linha da instância recém-criada. Um diálogo abre com o QR Code — escaneie com o WhatsApp do número que vai operar a instância (WhatsApp → Aparelhos conectados → Conectar um aparelho).
- Aguarde o status mudar para Conectada, depois clique em
Detalhes → aba Envio de teste. Preencha telefone e texto e
envie. Para o primeiro teste, recomendamos desmarcar "Envio
proativo" — o mesmo motivo do caminho por API abaixo: isola se um
eventual problema é do WhatsApp Web ou do
AntiBanGuard.
Caminho 2 — direto pela API
Todas as chamadas abaixo usam a API key global (AUTHENTICATION_API_KEY
do ambiente) no cabeçalho apikey. Ajuste http://localhost:8080 para o
host real do seu ambiente.
1. Criar a instância
POST /instance/create
- cURL
- Node
- Python
curl -X POST http://localhost:8080/instance/create \
-H "apikey: SUA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "minha-instancia"}'
// fetch e global no Node desde a v18 — nao precisa de dependencia extra.
const res = await fetch('http://localhost:8080/instance/create', {
method: 'POST',
headers: { apikey: 'SUA_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'minha-instancia' }),
});
console.log(await res.json());
# pip install requests
import requests
res = requests.post(
"http://localhost:8080/instance/create",
headers={"apikey": "SUA_API_KEY"},
json={"name": "minha-instancia"},
)
print(res.json())
Resposta 200: { id, name, status, webhookUrl, createdAt }, com
status: "disconnected" (ainda não pareada).
2. Pedir o QR Code
GET /instance/connect/:name
- cURL
- Node
- Python
curl http://localhost:8080/instance/connect/minha-instancia \
-H "apikey: SUA_API_KEY"
const res = await fetch('http://localhost:8080/instance/connect/minha-instancia', {
headers: { apikey: 'SUA_API_KEY' },
});
const { status, qr } = await res.json();
console.log(status, qr); // qr e uma data URL PNG (data:image/png;base64,...)
import requests
res = requests.get(
"http://localhost:8080/instance/connect/minha-instancia",
headers={"apikey": "SUA_API_KEY"},
)
dados = res.json()
print(dados["status"], dados["qr"]) # qr e uma data URL PNG
A resposta é { status, qr }, onde qr é uma data URL PNG
(data:image/png;base64,...) — cole no campo de endereço de um navegador
para visualizar, ou decodifique o base64 e salve como arquivo .png.
Escaneie com WhatsApp → Aparelhos conectados → Conectar um
aparelho. O mesmo QR também chega ao vivo pelo WebSocket /ws/:instance,
sem precisar dar poll nesta rota.
3. Aguardar a conexão
GET /instance/status/:name, repetido a cada poucos segundos até
status virar "connected".
- cURL
- Node
- Python
watch -n 3 curl -s http://localhost:8080/instance/status/minha-instancia \
-H "apikey: SUA_API_KEY"
async function aguardarConexao(nome) {
for (;;) {
const res = await fetch(`http://localhost:8080/instance/status/${nome}`, {
headers: { apikey: 'SUA_API_KEY' },
});
const { status } = await res.json();
if (status === 'connected') return;
await new Promise((r) => setTimeout(r, 3000));
}
}
await aguardarConexao('minha-instancia');
import time
import requests
def aguardar_conexao(nome):
while True:
res = requests.get(
f"http://localhost:8080/instance/status/{nome}",
headers={"apikey": "SUA_API_KEY"},
)
if res.json()["status"] == "connected":
return
time.sleep(3)
aguardar_conexao("minha-instancia")
4. Enviar a mensagem de teste
POST /message/sendText, com proactive: false. O caminho reativo
(proactive: false, o padrão) pula todos os gates do AntiBanGuard — se
essa primeira mensagem falhar, o problema é do WhatsApp Web/sessão, não do
anti-ban. Só depois de confirmar que o envio básico funciona faça sentido
testar proactive: true (o caminho usado por qualquer disparo de verdade,
sujeito a opt-in, blacklist, janela de horário e limite diário — ver
Arquitetura).
- cURL
- Node
- Python
curl -X POST http://localhost:8080/message/sendText \
-H "apikey: SUA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"instance": "minha-instancia",
"phone": "5565999998888",
"text": "Teste da API WhatsApp",
"proactive": false
}'
const res = await fetch('http://localhost:8080/message/sendText', {
method: 'POST',
headers: { apikey: 'SUA_API_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({
instance: 'minha-instancia',
phone: '5565999998888',
text: 'Teste da API WhatsApp',
proactive: false,
}),
});
console.log(await res.json());
import requests
res = requests.post(
"http://localhost:8080/message/sendText",
headers={"apikey": "SUA_API_KEY"},
json={
"instance": "minha-instancia",
"phone": "5565999998888",
"text": "Teste da API WhatsApp",
"proactive": False,
},
)
print(res.json())
Resposta 200: { status, id, reason? }, onde status é "queued"
(enfileirado — a entrega real acontece a seguir, no worker),
"scheduled" (adiado) ou "rejected" (barrado; só possível com
proactive: true, reason explica o gate). Nunca "sent": o envio
enfileirado ainda não aconteceu no instante em que a API responde.
Tudo acima é o que o código faz — mas o pareamento por QR Code com um
Chromium real (headless, dentro de um container) nunca foi observado
rodando ponta a ponta em produção neste projeto. Se o QR não aparecer, a
instância não conectar, ou o envio de teste falhar, você pode estar
encontrando o primeiro problema real desse fluxo. Consulte a seção de
Troubleshooting — além dela, os melhores sinais são
os logs do container (docker logs) e o campo reason da resposta de
sendText quando status é rejected.