DevZap · Guias de integração Voltar ao painel

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


Guia verificado em 05/08/2026 contra a API v1. Divergiu do comportamento real? Isso é um bug, o código é a verdade.


← Todos os guias