Primeiros passos
Objetivo: sair do zero e enviar uma mensagem de WhatsApp pela API. Leva cerca de 10 minutos, a maior parte esperando o QR code.
1. Obtenha uma chave de API
A chave tem o formato ten_ seguido de 64 caracteres e identifica o seu tenant: tudo que
você acessa pela API pertence a ele.
Pelo painel, em Chaves de API no menu lateral: Nova chave → dê um nome que identifique o sistema que vai usá-la. A tela existe em qualquer plano, e é também de onde você rotaciona e revoga. Quantas chaves cabem ao mesmo tempo depende do plano, e a tela informa.
Se você está construindo uma aplicação que conecta contas de terceiros, o caminho é outro:
OAuth 2.1, em que cada usuário autoriza o acesso sem lhe entregar chave nenhuma. O token que o
fluxo devolve é uma chave ten_. Veja OAuth 2.1, notando que ele precisa
estar habilitado na instalação.
A chave aparece uma única vez, no momento da criação. Guarde-a onde você guarda segredos, não no código. Perdeu o valor? Não precisa criar outra: Rotacionar, na mesma tela, troca o segredo mantendo o nome.
2. Confirme que a chave funciona
curl https://apidevzap.com/api/v1/status \
-H "Authorization: Bearer ten_sua_chave_aqui"
Resposta esperada:
{
"success": true,
"data": {
"tenant": { "id": "TENANT-XYZ", "name": "Sua Empresa", "status": "active" },
"apiKey": { "prefix": "ten_a1b2c3d", "name": "Integração CRM", "scopes": ["*"] },
"instances": { "total": 0, "connected": 0 }
}
}
Se vier 401, confira se copiou a chave inteira e se manteve o prefixo Bearer . Se vier 403
com TENANT_SUSPENDED, a conta está suspensa. É caso de falar com o suporte, não de tentar de
novo.
Este endpoint é barato e não consome cota de mensagens: use-o como health check da integração.
3. Crie uma instância
Uma instância representa um número de WhatsApp. Crie uma:
curl -X POST https://apidevzap.com/api/v1/instances \
-H "Authorization: Bearer ten_sua_chave_aqui" \
-H "Content-Type: application/json" \
-d '{"name": "Atendimento comercial"}'
A resposta traz o id no formato INST-xxx. Guarde: ele identifica a instância em todas as
chamadas seguintes.
Se você já sabe para onde quer receber as mensagens, dá para registrar o webhook na mesma chamada, e é a forma recomendada, porque a operação vira tudo-ou-nada:
curl -X POST https://apidevzap.com/api/v1/instances \
-H "Authorization: Bearer ten_sua_chave_aqui" \
-H "Content-Type: application/json" \
-d '{
"name": "Atendimento comercial",
"webhook": { "url": "https://seu-sistema.com.br/webhooks/whatsapp" }
}'
Como você não informou um secret, um é gerado e devolvido em claro nesta resposta: a única
vez que ele aparece. Guarde-o: é com ele que você valida as entregas.
Criar instância consome cota do plano. Se vier
403, ou a cota acabou ou não há assinatura ativa. Veja Limites e planos.
4. Conecte o WhatsApp
Aqui está a parte que costuma surpreender: a primeira chamada quase nunca devolve o QR code.
E quem dispara a conexão é esta chamada, por isso a chave precisa do escopo instances:manage
(a chave de acesso total do painel tem). Com uma chave só de consulta o /qr só lê e não liga o número.
curl https://apidevzap.com/api/v1/instances/INST-XXX/qr \
-H "Authorization: Bearer ten_sua_chave_aqui"
A primeira resposta normalmente é:
{ "success": true, "data": { "status": "initializing", "message": "Conexão inicializando..." } }
Isso é esperado, não é erro: a chamada dispara a conexão além de consultá-la, e subir o motor leva alguns segundos. Chame de novo depois de uns 3 segundos e você recebe:
{
"success": true,
"data": {
"status": "qr_code",
"qrCode": "2@A1B2C3...",
"expiresAt": "2026-08-05T18:30:00.000Z"
}
}
Renderize o conteúdo de qrCode como um QR code de verdade (qualquer biblioteca serve, é só uma
string) e escaneie no celular: WhatsApp → Aparelhos conectados → Conectar aparelho.
Depois do pareamento, a mesma chamada passa a devolver status: "connected" com o número em
phone. Um laço de polling simples:
async function conectar(instanceId, chave) {
for (let tentativa = 0; tentativa < 40; tentativa++) {
const r = await fetch(`https://apidevzap.com/api/v1/instances/${instanceId}/qr`, {
headers: { Authorization: `Bearer ${chave}` },
});
const { data } = await r.json();
if (data.status === 'connected') return data.phone;
if (data.status === 'qr_code') mostrarQrParaUsuario(data.qrCode);
// 'initializing' → só esperar
await new Promise((r) => setTimeout(r, 3000));
}
throw new Error('tempo esgotado aguardando a conexão');
}
O QR expira (veja expiresAt). Quando isso acontece, a chamada seguinte devolve um novo, por isso
o laço acima continua funcionando sem tratamento especial.
5. Envie a primeira mensagem
{
"to": "5511123456789",
"message": "Olá! Identificamos um débito em aberto. Podemos conversar?"
}
curl -X POST https://apidevzap.com/api/v1/instances/INST-XXX/messages/send \
-H "Authorization: Bearer ten_sua_chave_aqui" \
-H "Content-Type: application/json" \
-d '{"to": "5511123456789", "message": "Olá! Chegou?"}'
Resposta:
{
"success": true,
"data": {
"messageId": "true_5511123456789@c.us_3EB0ABC123",
"to": "5511123456789",
"type": "text",
"status": "sent"
},
"message": "Mensagem enviada com sucesso"
}
Guarde o messageId: é ele que aparece depois nos eventos e serve para deduplicação.
Erros comuns na estreia
401 em tudo. A chave não foi copiada inteira, ou faltou o Bearer . Vale conferir também se
não sobrou um espaço no fim ao colar.
400 com body must have required property 'message'. O texto vai em message, na raiz do
objeto, não dentro de content. Se você viu um exemplo com content: { text: ... } em algum
lugar, ele está desatualizado; aquele formato nunca funcionou.
400 de validação em to. O número aceita de 10 a 15 dígitos, sem +, espaço ou traço.
5511123456789 funciona; +55 11 12345-6789 não.
400 com INSTANCE_NOT_CONNECTED. A instância perdeu a sessão. Volte ao passo 4, normalmente
basta reconectar, sem QR novo.
404 ao enviar. O INST-xxx está errado, ou pertence a outro tenant. Confira em
GET /api/v1/instances.
Próximos passos
- Webhooks: para receber as respostas dos seus contatos.
- Erros e retry: o que fazer quando falha, e o que não adianta retentar.
- Envio de mensagens: mídia, documentos e envio com garantia de entrega.
Guia verificado em 05/08/2026 contra a API v1. Divergiu do comportamento real? Isso é um bug, o código é a verdade.