API e integrações
Crie chaves de API com permissões mínimas, chame qualquer operação do painel por HTTP, receba eventos em tempo real e faça operações pagas com cotação.
Para que serve
A API permite que outros sistemas — ERP, loja virtual, n8n, um script seu — façam pelo Zynzap o mesmo que você faz no painel: listar dispositivos, enviar mensagens, criar contatos, iniciar campanhas, ouvir eventos. Ela usa as mesmas permissões e regras de orçamento do painel.
Cada integração usa uma chave pessoal, com permissões explícitas, números e IPs permitidos, limite por minuto, validade, rotação e revogação a qualquer momento, além de estatísticas de uso e webhooks assinados.
Antes de começar
- Ative a autenticação em duas etapas em Perfil e segurança: ela é obrigatória para criar chaves.
- Qualquer papel pode criar chaves, mas a chave nunca passa das permissões do seu papel. Administradores veem e revogam as chaves de toda a empresa.
Criar uma chave
Em API e integrações, clique em Nova chave. Um painel lateral abre com cinco etapas (no celular, ele sobe da parte de baixo da tela). Dá para voltar a qualquer etapa já visitada pela barra de progresso.
1. Nome e finalidade
- Nome: só para você reconhecer a chave. Ex.: "ERP da loja" ou "n8n produção". Use um nome por sistema.
- Finalidade (opcional): para que a integração usa a API e quem é o responsável.
- Ambiente: Produção ou Teste. É só um rótulo para organizar (o acesso é o mesmo), mas o segredo começa com
zz_live_ouzz_test_, o que evita trocas por engano. Chaves criadas antes da mudança de nome (wl_live_/wl_test_) continuam funcionando.
2. Permissões
Comece por um modelo e ajuste se precisar:
| Modelo | Permissões |
|---|---|
| Somente leitura | Ler números, ler grupos, receber eventos, ler painel |
| Envio de mensagens | Ler números, enviar mensagens, receber eventos |
| Atendimento | Ler números, Atendimento, enviar/apagar mensagens, visualização única, receber eventos |
| Campanhas | Ler números, receber eventos, campanhas, contatos, modelos, mídias |
| Administração completa | Tudo o que o seu papel permite |
As permissões ficam agrupadas (Mensagens, Números e grupos, Eventos e painel, Atendimento, Produto), cada uma com a descrição do que libera:
| Permissão | O que libera |
|---|---|
dashboard:read | Lê os números do painel (Visão geral) |
events:read | Recebe eventos em tempo real (mensagens, status de entrega, conexão) |
messages:send | Envia mensagens de texto e mídia (envios pagos consomem saldo) |
messages:revoke | Apaga para todos uma mensagem já enviada |
messages:view_once | Lê e gerencia mídias de visualização única recebidas |
sessions:read / sessions:manage | Lista números / conecta, desconecta, retoma e exclui |
groups:read / groups:manage | Lista grupos / altera participantes, nome, descrição e permissões |
product:inbox | Lê e responde conversas do Atendimento, notas e chamadas |
product:contacts | Contatos e dados do CRM |
product:campaigns | Campanhas, listas, segmentos e recuperação |
product:warmup | Aquecimento de números |
product:media | Biblioteca de mídias |
product:templates | Modelos de mensagem e diálogos |
product:flows | Fluxos (bots) |
product:ai | Assistente de IA |
A chave nunca recebe mais do que o seu papel permite: permissões fora do seu papel aparecem desativadas e um modelo aplica só a parte permitida.
3. Números e limites
- Números que a chave acessa: todos os números ou somente os selecionados (a chave só enxerga esses números e os grupos e eventos deles). Ex.: um parceiro que opera só um número. Permissões que valem para a conta inteira (painel, contatos, campanhas, mídias, modelos, fluxos, aquecimento e IA) não combinam com números restritos. Atendentes sempre escolhem entre os números liberados para eles.
- Permitir criar e parear números: a integração cria números e gera o QR Code/código de pareamento (liga
sessions:manage). Informe o máximo de números desta chave; cada número ocupa uma vaga do plano. - Limite de requisições por minuto (opcional): teto próprio da chave, de 1 a 120. Acima dele a API responde
429comreason: API_KEY_RATE_LIMITED. O limite da conta (120 por minuto somando todas as chaves) continua valendo. - IPs permitidos (opcional): só chamadas desses endereços são aceitas; as demais recebem
403comreason: API_KEY_IP_FORBIDDEN. Aceita IPv4, IPv6 e faixas CIDR (ex.:203.0.113.10ou198.51.100.0/24), até 50. Em branco, qualquer origem.
4. Validade
30 dias, 90 dias, 1 ano, uma data personalizada (até 3 anos) ou nunca expira. Depois da data as chamadas recebem erro de autenticação. Prefira prazos definidos e rotacione antes de vencer.
5. Revisão e segredo
Confira o resumo (cada bloco tem um lápis para voltar à etapa) e clique em Criar chave. Se a última confirmação de identidade tiver mais de 10 minutos, o painel pede o código do autenticador.
O segredo aparece uma única vez (é guardado só como hash), com botão de copiar e exemplos prontos em cURL, Node.js e Python já com o endereço do seu painel e a chave. Para fechar, marque Eu salvei a chave em um local seguro.

Acompanhar e gerenciar as chaves
A lista mostra, para cada chave: nome, prefixo (o segredo fica mascarado), ambiente, quem criou e quando, resumo das permissões, números, IPs e limite, uso nas últimas 24 horas (minigráfico por hora, com erros), último uso (com o IP) e status: Ativa, Expira em breve (menos de 7 dias), Expirada ou Revogada. No topo ficam os totais de chaves ativas, requisições, erros e recusas das últimas 24 horas. Busque por nome, prefixo ou criador e filtre por status ou permissão.
Clique em uma chave (ou em ⋯ → Detalhes) para abrir o painel de detalhes: uso diário dos últimos 30 dias, erros e recusas das últimas 24 horas, permissões, números, IPs, limite, validade e a linha do tempo (criação, alterações, rotações e revogação, com quem fez).

- Editar nome e limites: muda nome, finalidade, ambiente, limite por minuto, IPs permitidos e o máximo de números, sem trocar o segredo. Permissões e números de uma chave existente não mudam: crie outra chave para isso.
- Rotacionar segredo: gera um segredo novo para a mesma chave (permissões, limites e histórico continuam). Escolha quando o segredo atual para de funcionar: imediatamente, em 1 hora ou em 24 horas. Durante essa carência os dois segredos funcionam; uma nova rotação encerra o segredo antigo que ainda estava em carência.
- Revogar: encerra o acesso na hora, inclusive streams de eventos abertos e um segredo antigo em carência. Não dá para desfazer.
Criar, editar e rotacionar exigem segundo fator ativo e confirmação recente de identidade. Tudo fica registrado na auditoria de segurança da empresa.
Fazendo chamadas
Todas as chamadas usam o cabeçalho Authorization: Bearer SUA_CHAVE.
GET /api/v1— descreve a API: lista de operações disponíveis, limites e rotas.POST /api/v1/rpc/NOME_DA_OPERACAO— executa uma operação, com o corpo{"request":{...}}. Os campos seguem o contrato gRPC emsnake_case.
curl https://SEU_PAINEL/api/v1/rpc/ListSessions \
-H 'Authorization: Bearer SUA_CHAVE' \
-H 'Content-Type: application/json' \
-d '{"request":{"pagination":{"page":1,"page_size":20}}}'Exemplos de operações: ListSessions, ListContacts, CreateContact, SendMessage, ListConversations, SendConversationMessage, StartCampaign, ListCrmOpportunities. A lista completa vem em GET /api/v1.
Limites: 120 solicitações por minuto por conta (e o limite próprio da chave, se houver), corpo de até 12 MiB e prazo de até 30 segundos. Envie deadlineMs (1 a 30000) para um prazo menor.
Também é possível usar gRPC diretamente, com o mesmo cabeçalho Authorization: Bearer SUA_CHAVE. As definições TypeScript do contrato HTTP ficam em src/lib/public-api-types.ts no código do painel.
Operações pagas: cotação antes
Envios, campanhas, aquecimento (inclusive retomar) e IA exigem uma cotação válida:
- Peça a cotação em
POST /api/v1/billing/quotescom{"method":"SendMessage","request":{...}}. - Execute a operação com
"billing":{"quoteId":"ID_DA_COTACAO"}ao lado derequest(ou, no gRPC, o cabeçalhox-whatslab-billing-quote: ID_DA_COTACAO).
# 1) cotação
curl https://SEU_PAINEL/api/v1/billing/quotes \
-H 'Authorization: Bearer SUA_CHAVE' -H 'Content-Type: application/json' \
-d '{"method":"SendMessage","request":{"session_id":"ID_DO_DISPOSITIVO","to_jid":"5511900000000","text":"Pedido enviado!"}}'
# 2) envio com a cotação
curl https://SEU_PAINEL/api/v1/rpc/SendMessage \
-H 'Authorization: Bearer SUA_CHAVE' -H 'Content-Type: application/json' \
-d '{"request":{"session_id":"ID_DO_DISPOSITIVO","to_jid":"5511900000000","text":"Pedido enviado!"},"billing":{"quoteId":"ID_DA_COTACAO"}}'A cotação fica vinculada ao conteúdo, aos dispositivos e à configuração: alterar o conteúdo exige outra cotação. O atalho POST /api/v1/messages/send aceita o mesmo corpo de SendMessage.
Mensagem aceita pelo transporte pode gerar consumo, mas aceitação não confirma entrega nem leitura. Em resultado incerto, confira o estado antes de reenviar — não repita às cegas.
Eventos em tempo real
GET /api/v1/events (com events:read) abre um stream SSE com mensagens recebidas, status de entrega, conexão dos números e mais.
- Cada evento persistido traz um
id. Para continuar de onde parou, reconecte com o cabeçalhoLast-Event-IDou?after_cursor=CURSOR. - O evento
refreshpede uma nova consulta quando o histórico já expirou. - Filtre dispositivos repetindo
session_id. Chaves sem restrição de dispositivos podem usarinclude_product=truepara eventos de campanhas e bibliotecas. - O stream valida a chave continuamente e encerra quando ela é revogada. Eventos de pareamento são transitórios e não ficam no histórico.
curl -N https://SEU_PAINEL/api/v1/events \
-H 'Authorization: Bearer SUA_CHAVE' \
-H 'Last-Event-ID: ULTIMO_ID_RECEBIDO'Webhooks
Em vez de manter o stream aberto, a chave pode enviar os eventos para o seu sistema: o Zynzap faz um POST com JSON para o endereço cadastrado a cada evento escolhido. Abra a chave em API e integrações e use a seção Webhooks do painel de detalhes.
- Até 5 endereços por chave, cada um com os seus eventos. Só aparecem os eventos que a chave já poderia ler em
/api/v1/events: exige a permissão Receber eventos, respeita os números da chave e eventos da conta inteira (campanhas e aquecimento) só valem para chaves sem restrição de números. Se a chave for revogada, vencer ou perder a permissão, as entregas param na hora. - Endereço:
https://público, porta 443 ou 8443, sem usuário e senha. Endereços locais, de rede privada, link-local ou de metadados de nuvem são recusados no cadastro e em cada entrega (o domínio é resolvido de novo e fixado; redirecionamentos não são seguidos). - Segredo de assinatura (
whsec_…): aparece uma única vez ao criar. Para trocar, use Novo segredo: com carência de 24 horas, as entregas levam as duas assinaturas até o fim dela. - Enviar teste manda um evento
webhook.pingsintético (nenhuma mensagem é enviada pelo WhatsApp). O resultado aparece no Registro: status HTTP, tempo de resposta, tentativas e o motivo da falha das últimas entregas (o conteúdo não fica guardado). - Ativar/desativar a qualquer momento. Ao reativar, a entrega recomeça a partir daquele momento.
O que chega
POST /seu-endpoint HTTP/1.1
Content-Type: application/json
User-Agent: Zynzap-Webhooks/1
X-Zynzap-Event-Id: 6f1c… (mesmo valor de "id"; use para ignorar repetições)
X-Zynzap-Event-Type: MessageReceived
X-Zynzap-Delivery-Id: 0b9e…
X-Zynzap-Attempt: 1
X-Zynzap-Timestamp: 1760280000
X-Zynzap-Signature: v1=5d41… (durante a carência de rotação: v1=…,v1=…)
{"id":"6f1c…","type":"MessageReceived","created_at":"2026-10-12T14:00:00.000Z","cursor":"1042",
"session_id":"ID_DO_NUMERO","webhook_id":"…","data":{"MessageReceived":{…}}}data é o mesmo JSON dos eventos domain de /api/v1/events. cursor permite ordenar e, se precisar, buscar o que faltou no stream com ?after_cursor=.
Responda com qualquer 2xx em até 10 segundos e processe depois (fila). Respostas fora de 2xx, redirecionamentos, tempo esgotado ou falha de conexão contam como falha: o Zynzap tenta de novo após 30 s, 2 min, 10 min, 30 min, 1 h, 2 h e 4 h (8 tentativas, cerca de 8 horas). Depois de 15 falhas seguidas o webhook é desativado, a linha do tempo da chave registra o motivo e ele aparece como Desativado por falhas. A entrega é pelo menos uma vez: o mesmo id pode chegar mais de uma vez e a ordem não é garantida.
Conferir a assinatura
A assinatura é HMAC-SHA256(segredo, "{X-Zynzap-Timestamp}.{corpo bruto}") em hexadecimal, com o prefixo v1=. Calcule sobre o corpo bruto (antes de interpretar o JSON), compare em tempo constante e recuse entregas com mais de 5 minutos para se proteger de reenvio (replay). Aceite a entrega se qualquer v1= conferir.
import crypto from "node:crypto";
// rawBody: corpo BRUTO da requisição (string), antes de JSON.parse.
export function verifyZynzap(rawBody, headers, secret = process.env.ZYNZAP_WEBHOOK_SECRET) {
const timestamp = headers["x-zynzap-timestamp"];
// Recuse entregas com mais de 5 minutos (proteção contra replay).
if (!/^\d+$/.test(timestamp ?? "") || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
return (headers["x-zynzap-signature"] ?? "").split(",").some((part) => {
const [scheme, value = ""] = part.trim().split("=");
return scheme === "v1" && value.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(value), Buffer.from(expected));
});
}
// Express: app.post("/zynzap", express.raw({ type: "application/json" }), (req, res) => {
// if (!verifyZynzap(req.body.toString("utf8"), req.headers)) return res.sendStatus(401);
// res.sendStatus(204); // responda rápido; processe numa fila
// });import hashlib, hmac, os, time
# raw_body: corpo BRUTO da requisição (bytes), antes de json.loads.
def verify_zynzap(raw_body: bytes, headers, secret: str = os.environ["ZYNZAP_WEBHOOK_SECRET"]) -> bool:
timestamp = headers.get("X-Zynzap-Timestamp", "")
# Recuse entregas com mais de 5 minutos (proteção contra replay).
if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
return False
expected = hmac.new(secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
for part in headers.get("X-Zynzap-Signature", "").split(","):
scheme, _, value = part.strip().partition("=")
if scheme == "v1" and hmac.compare_digest(value, expected):
return True
return False
# Flask: if not verify_zynzap(request.get_data(), request.headers): abort(401)Guarde o segredo como qualquer senha (variável de ambiente ou cofre). Se ele vazar, gere um novo segredo; se o endereço vazar, a assinatura impede que terceiros se passem pelo Zynzap.
Pareamento e mídias
POST /api/v1/sessions/createePOST /api/v1/sessions/resume— criam ou retomam um dispositivo e devolvem o QR Code/código de pareamento por SSE.GetMediaUrldevolve um caminho autenticado; baixe os bytes emGET /api/v1/media/IDcom Bearer eproduct:media.
Erros
Erros voltam como { "error": { "code", "message", "requestId", "reason" } } e com o cabeçalho x-request-id.
| Código | HTTP | Quando acontece |
|---|---|---|
UNAUTHENTICATED | 401 | Chave inválida, vencida ou revogada |
FORBIDDEN | 403 | A chave não tem a permissão necessária ou o IP não está na lista da chave (reason: API_KEY_IP_FORBIDDEN) |
NOT_FOUND | 404 | Recurso inexistente ou fora do alcance da chave |
VALIDATION_FAILED | 400/413 | Requisição inválida ou grande demais |
CONFLICT | 409 | Estado conflitante ou cotação expirada |
PAYMENT_REQUIRED | 402/428 | Cotação ausente ou saldo insuficiente (veja reason) |
RATE_LIMITED | 429 | Mais de 120 solicitações por minuto na conta ou acima do limite da chave (reason: API_KEY_RATE_LIMITED) |
UNAVAILABLE | 503/504 | Indisponibilidade temporária |
INTERNAL | 500 | Erro inesperado — informe o requestId ao suporte |
Dicas
- Uma chave por sistema, com o mínimo de permissões, validade definida e, quando o sistema tiver IP fixo, lista de IPs permitidos.
- Rotacione periodicamente com carência de 1 ou 24 horas para trocar o segredo sem parar a integração.
- Use chaves de Teste (
zz_test_…) em desenvolvimento e de Produção (zz_live_…) só no sistema real. - Em
429, espere e tente de novo com intervalo crescente (1 s, 2 s, 4 s…). - Guarde o
requestIddos erros: ele permite ao suporte localizar a chamada.
Perguntas frequentes
Permissões
| Papel | O que pode fazer |
|---|---|
| Administrador | Cria chaves com qualquer permissão e revoga chaves de toda a empresa |
| Gestor | Cria e revoga as próprias chaves, até as permissões do papel |
| Atendente | Cria e revoga as próprias chaves, até as permissões do papel |
| Leitor | Cria e revoga as próprias chaves, apenas de leitura |
Cobrança
Chamadas de leitura são gratuitas. Operações pagas (envios, campanhas, aquecimento, IA) cobram exatamente como no painel, sempre a partir de uma cotação. Veja Cobrança.