Zynzap · Ajuda
Gestão da conta

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_ ou zz_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:

ModeloPermissões
Somente leituraLer números, ler grupos, receber eventos, ler painel
Envio de mensagensLer números, enviar mensagens, receber eventos
AtendimentoLer números, Atendimento, enviar/apagar mensagens, visualização única, receber eventos
CampanhasLer números, receber eventos, campanhas, contatos, modelos, mídias
Administração completaTudo 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ãoO que libera
dashboard:readLê os números do painel (Visão geral)
events:readRecebe eventos em tempo real (mensagens, status de entrega, conexão)
messages:sendEnvia mensagens de texto e mídia (envios pagos consomem saldo)
messages:revokeApaga para todos uma mensagem já enviada
messages:view_onceLê e gerencia mídias de visualização única recebidas
sessions:read / sessions:manageLista números / conecta, desconecta, retoma e exclui
groups:read / groups:manageLista grupos / altera participantes, nome, descrição e permissões
product:inboxLê e responde conversas do Atendimento, notas e chamadas
product:contactsContatos e dados do CRM
product:campaignsCampanhas, listas, segmentos e recuperação
product:warmupAquecimento de números
product:mediaBiblioteca de mídias
product:templatesModelos de mensagem e diálogos
product:flowsFluxos (bots)
product:aiAssistente 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 429 com reason: 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 403 com reason: API_KEY_IP_FORBIDDEN. Aceita IPv4, IPv6 e faixas CIDR (ex.: 203.0.113.10 ou 198.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.

Painel lateral Nova chave de API na etapa 2 de 5 (Permissões), com o modelo Envio de mensagens selecionado e as permissões agrupadas em Mensagens e Números e grupos
Permissões mínimas, validade definida e, se possível, restrição de números e IPs.

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).

Página API e integrações com totais de chaves ativas, requisições, erros e recusas nas últimas 24 horas, busca, filtros de status e permissão e a lista de chaves com prefixo mascarado, ambiente, permissões, minigráfico de uso, último uso e status
Cada sistema com a sua chave: fica fácil revogar só o que precisa.
  • 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 em snake_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:

  1. Peça a cotação em POST /api/v1/billing/quotes com {"method":"SendMessage","request":{...}}.
  2. Execute a operação com "billing":{"quoteId":"ID_DA_COTACAO"} ao lado de request (ou, no gRPC, o cabeçalho x-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çalho Last-Event-ID ou ?after_cursor=CURSOR.
  • O evento refresh pede uma nova consulta quando o histórico já expirou.
  • Filtre dispositivos repetindo session_id. Chaves sem restrição de dispositivos podem usar include_product=true para 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.ping sinté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/create e POST /api/v1/sessions/resume — criam ou retomam um dispositivo e devolvem o QR Code/código de pareamento por SSE.
  • GetMediaUrl devolve um caminho autenticado; baixe os bytes em GET /api/v1/media/ID com Bearer e product:media.

Erros

Erros voltam como { "error": { "code", "message", "requestId", "reason" } } e com o cabeçalho x-request-id.

CódigoHTTPQuando acontece
UNAUTHENTICATED401Chave inválida, vencida ou revogada
FORBIDDEN403A 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_FOUND404Recurso inexistente ou fora do alcance da chave
VALIDATION_FAILED400/413Requisição inválida ou grande demais
CONFLICT409Estado conflitante ou cotação expirada
PAYMENT_REQUIRED402/428Cotação ausente ou saldo insuficiente (veja reason)
RATE_LIMITED429Mais de 120 solicitações por minuto na conta ou acima do limite da chave (reason: API_KEY_RATE_LIMITED)
UNAVAILABLE503/504Indisponibilidade temporária
INTERNAL500Erro 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 requestId dos erros: ele permite ao suporte localizar a chamada.

Perguntas frequentes

Permissões

PapelO que pode fazer
AdministradorCria chaves com qualquer permissão e revoga chaves de toda a empresa
GestorCria e revoga as próprias chaves, até as permissões do papel
AtendenteCria e revoga as próprias chaves, até as permissões do papel
LeitorCria 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.