Pular para o conteúdo
ZapHub

Documentação

API /v1

Tudo o que o painel faz com números, mensagens, campanhas, leads e economia está na API: coloque a conexão WhatsApp dentro do seu CRM, ERP ou sistema de atendimento. São 85 endpoints REST, mais os eventos por webhook, RabbitMQ, NATS, Kafka, SQS ou WebSocket.

Visão geral

  • Base URL: https://api.zaphub.wssengenharia.com.br/v1 (HTTPS apenas). Corpo e respostas em JSON UTF-8.
  • Ids: sempre identificadores públicos (strings). Nenhum id interno sai da API.
  • Datas: ISO 8601 em UTC. Valores: sempre em centavos de real (inteiros).
  • Telefones: aceite máscara (+55 (11) 98765-4321); sem DDI e com 10 ou 11 dígitos assumimos o Brasil. A API devolve E.164 sem o + (5511987654321).
  • Cobrança: somente por número conectado. Usuários, sistemas integrados e chamadas de API não são cobrados (respeitado o limite de taxa).
  • Números: criar e remover números continua no painel, pois ocupa uma vaga do plano e guarda credenciais da Meta. Todo o resto, inclusive conectar por QR, está na API.

As conexões não oficiais do ZapHub são operadas com Evolution Go (Evolution Foundation).

Início rápido

Quatro passos: criar a chave, conectar um número, enviar e receber o evento. Os exemplos em JavaScript e PHP usam o helper zaphub() da seção Autenticação.

1. Crie uma chave de API

No painel, em Integrações, crie e nomeie uma chave. Copie o valor completo (zh_live_…): ele aparece uma única vez; depois só o prefixo fica visível. Guarde-o em uma variável de ambiente (ZAPHUB_KEY). Para descobrir o numeroId do número criado no painel, chame GET /v1/numeros.

2. Conecte um número por QR Code

O número é criado no painel (ele ocupa uma vaga do plano). Pela API você pede o QR, mostra no seu sistema e acompanha o pareamento pelos eventos numero.qrcode e numero.status.

# QR Code (somente números NAO_OFICIAL)
curl -X POST https://api.zaphub.wssengenharia.com.br/v1/numeros/clx0numero000000000000001/conectar \
  -H "Authorization: Bearer $ZAPHUB_KEY"

# ...ou o código de pareamento de 8 caracteres, sem escanear nada
curl -X POST https://api.zaphub.wssengenharia.com.br/v1/numeros/clx0numero000000000000001/conectar \
  -H "Authorization: Bearer $ZAPHUB_KEY" \
  -H "Content-Type: application/json" \
  -d '{"telefone":"5511987654321"}'
import { zaphub } from "./zaphub.js";

const numeroId = "clx0numero000000000000001";
const conexao = await zaphub("POST", "/v1/numeros/" + numeroId + "/conectar");

// conexao.modo === "QR"
// conexao.qrcode -> data URL PNG: <img src={conexao.qrcode}>
// conexao.codigo -> texto do QR, se preferir desenhar o QR no seu sistema
// conexao.expiraEm -> depois disso o QR deixa de ser renovado; chame "conectar" de novo
// QRs renovados chegam pelo evento numero.qrcode; numero.status com "CONECTADO" confirma.
<?php
require 'zaphub.php';

$numeroId = 'clx0numero000000000000001';
[$status, $conexao] = zaphub('POST', "/v1/numeros/$numeroId/conectar");

// $conexao['modo'] === 'QR'
// $conexao['qrcode']  -> data URL PNG: <img src="<?= $conexao['qrcode'] ?>">
// $conexao['codigo']  -> texto do QR
// QRs renovados chegam pelo evento numero.qrcode; numero.status com CONECTADO confirma.

3. Envie uma mensagem

A resposta é 202: a mensagem foi aceita e o resultado final chega no evento mensagem.status. Envie o header Idempotency-Key para poder repetir a requisição sem enviar duas vezes.

curl -X POST https://api.zaphub.wssengenharia.com.br/v1/mensagens/texto \
  -H "Authorization: Bearer $ZAPHUB_KEY" \
  -H "Idempotency-Key: pedido-1042" \
  -H "Content-Type: application/json" \
  -d '{"numeroId":"clx0numero000000000000001","para":"5511987654321","texto":"Olá! Seu pedido saiu para entrega."}'

# 202 Accepted
# {"id":"clx0mensagem0000000000001","tipoNumero":"NAO_OFICIAL","tipoEnvio":"PRIMEIRO_CONTATO",
#  "status":"ENVIADA","enfileirada":false,"contatoOptout":false,"janelaAberta":false,
#  "custoEvitado":{"marketingCentavos":34,"utilityCentavos":7}}
import { zaphub } from "./zaphub.js";

const msg = await zaphub(
  "POST",
  "/v1/mensagens/texto",
  { numeroId: "clx0numero000000000000001", para: "5511987654321", texto: "Olá! Seu pedido saiu para entrega." },
  { "Idempotency-Key": "pedido-1042" },
);
// msg.status: "ENVIADA" | "ENFILEIRADA" | "FALHOU" (com msg.erro.codigo / msg.erro.mensagem)
// msg.custoEvitado: quanto custaria na API Oficial (só números não oficiais)
<?php
require 'zaphub.php';

[$status, $msg] = zaphub('POST', '/v1/mensagens/texto', [
    'numeroId' => 'clx0numero000000000000001',
    'para' => '5511987654321',
    'texto' => 'Olá! Seu pedido saiu para entrega.',
], ['Idempotency-Key: pedido-1042']);
// $status === 202; $msg['status']: ENVIADA | ENFILEIRADA | FALHOU (com $msg['erro'])

4. Receba os eventos

Cadastre um destino por webhook (HTTPS) ou RabbitMQ. O segredo de assinatura aparece uma única vez na resposta (segredoHmacRevelado): guarde-o.

curl -X POST https://api.zaphub.wssengenharia.com.br/v1/destinos-eventos \
  -H "Authorization: Bearer $ZAPHUB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tipo": "WEBHOOK",
    "nome": "Meu CRM",
    "url": "https://crm.exemplo.com.br/hooks/zaphub",
    "eventos": ["mensagem.recebida", "mensagem.status", "lead.criado"]
  }'

# 201 -> { "id": "...", "segredoHmacRevelado": "whsec_...", ... }
import { zaphub } from "./zaphub.js";

const destino = await zaphub("POST", "/v1/destinos-eventos", {
  tipo: "WEBHOOK",
  nome: "Meu CRM",
  url: "https://crm.exemplo.com.br/hooks/zaphub",
  eventos: ["mensagem.recebida", "mensagem.status", "lead.criado"],
});
console.log(destino.segredoHmacRevelado); // whsec_... - guarde agora, não aparece de novo
<?php
require 'zaphub.php';

[$status, $destino] = zaphub('POST', '/v1/destinos-eventos', [
    'tipo' => 'WEBHOOK',
    'nome' => 'Meu CRM',
    'url' => 'https://crm.exemplo.com.br/hooks/zaphub',
    'eventos' => ['mensagem.recebida', 'mensagem.status', 'lead.criado'],
]);
echo $destino['segredoHmacRevelado']; // whsec_... - guarde agora, não aparece de novo

Quando o número conclui o pareamento você recebe numero.status com CONECTADO; a partir daí mensagens recebidas chegam em mensagem.recebida e o resultado de cada envio em mensagem.status. Em número OFICIAL, tudo é encaminhado à Meta, que aplica as regras dela (por exemplo, texto livre fora da janela de 24h): a recusa volta como FALHOU, com o código e a mensagem de erro da Meta. Em NAO_OFICIAL, a saída é cadenciada em fila no ritmo do número e nunca recusada por volume.

Autenticação

Envie a chave no header Authorization: Bearer <chave> em toda requisição.

  • As chaves têm o formato zh_live_…, são guardadas só como hash e podem ser revogadas a qualquer momento (a próxima requisição já recebe 401). Até 10 chaves ativas por conta.
  • Uma chave pode ser restrita a números: usar um número fora do conjunto responde 403; um número de outra conta responde 404. Chaves restritas não gerenciam destinos de eventos (configuração da conta inteira).
  • Conta com assinatura suspensa ou cancelada: 402 em toda rota, até regularizar o pagamento.

Os exemplos deste guia usam este helper:

export ZAPHUB_KEY="zh_live_..."   # a chave completa, copiada no painel

curl https://api.zaphub.wssengenharia.com.br/v1/numeros \
  -H "Authorization: Bearer $ZAPHUB_KEY"
// zaphub.js (Node 20+ ou qualquer runtime com fetch)
const BASE = "https://api.zaphub.wssengenharia.com.br";

export async function zaphub(metodo, caminho, corpo, cabecalhos = {}) {
  const r = await fetch(BASE + caminho, {
    method: metodo,
    headers: {
      Authorization: "Bearer " + process.env.ZAPHUB_KEY,
      "Content-Type": "application/json",
      ...cabecalhos,
    },
    body: corpo === undefined ? undefined : JSON.stringify(corpo),
  });
  const dados = r.status === 204 ? null : await r.json();
  if (!r.ok) {
    // { statusCode, code, message, fields? }: ramifique por "code", não por "message".
    throw Object.assign(new Error(dados.message), dados, { status: r.status, retryAfter: r.headers.get("retry-after") });
  }
  return dados;
}
<?php
// zaphub.php (PHP 8 com a extensão curl)
const ZAPHUB_BASE = 'https://api.zaphub.wssengenharia.com.br';

/** Devolve [status HTTP, corpo decodificado]. Erros vêm como { statusCode, code, message, fields }. */
function zaphub(string $metodo, string $caminho, ?array $corpo = null, array $cabecalhos = []): array
{
    $ch = curl_init(ZAPHUB_BASE . $caminho);
    curl_setopt_array($ch, [
        CURLOPT_CUSTOMREQUEST => $metodo,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTPHEADER => array_merge([
            'Authorization: Bearer ' . getenv('ZAPHUB_KEY'),
            'Content-Type: application/json',
        ], $cabecalhos),
        CURLOPT_POSTFIELDS => $corpo === null ? null : json_encode($corpo),
    ]);
    $resposta = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    return [$status, $resposta === '' || $resposta === false ? null : json_decode($resposta, true)];
}

Limites e idempotência

Limite de taxa

  • 600 requisições por minuto por chave, em janela deslizante. Toda resposta traz X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset (epoch, em segundos).
  • Acima disso: 429 com Retry-After (segundos).
  • Não há limite por volume de mensagens. Números NAO_OFICIAL são cadenciados com fila no ritmo do número (enfileirada: true, nunca 429); os OFICIAL seguem a vazão da Meta (padrão 80 mensagens por segundo).
  • Corpo máximo: 10 MB (cerca de 50.000 contatos em uma campanha).
async function comRetentativa(fn, tentativas = 5) {
  for (let i = 0; ; i++) {
    try {
      return await fn();
    } catch (e) {
      if (e.status !== 429 || i >= tentativas) throw e;
      await new Promise((ok) => setTimeout(ok, (Number(e.retryAfter) || 1) * 1000));
    }
  }
}
function comRetentativa(callable $fn, int $tentativas = 5): array
{
    for ($i = 0; ; $i++) {
        [$status, $corpo] = $fn();
        if ($status !== 429 || $i >= $tentativas) return [$status, $corpo];
        sleep(1); // ou leia o header Retry-After
    }
}

Idempotência

Envie o header Idempotency-Key (até 255 caracteres; um UUID serve) nos POST de criação: /v1/mensagens/*, /v1/campanhas e /v1/destinos-eventos.

  • Repetir a mesma requisição em até 24 horas devolve a resposta original (mesmo status e corpo), com o header Idempotent-Replayed: true, sem executar de novo.
  • A mesma chave com corpo diferente responde 422 idempotencia_corpo_diferente.
  • Duas requisições simultâneas com a mesma chave executam uma vez: a segunda espera e recebe a mesma resposta.
  • Só respostas de sucesso são guardadas; se a primeira tentativa falhar (erro de validação, por exemplo), a chave é liberada para a repetição corrigida.

Erros

Toda resposta de erro tem o mesmo formato. Ramifique por code, nunca por message (texto para humanos, em português).

{
  "statusCode": 400,
  "code": "validacao",
  "message": "Dados inválidos.",
  "fields": {
    "para": [
      "Telefone com poucos dígitos (informe DDD + número)."
    ]
  }
}
HTTPcodeQuando
400validacaoCorpo ou parâmetros inválidos. O objeto fields diz o que corrigir.
400destino_invalidoDestino de evento recusado: URL sem HTTPS, host interno/privado, broker sem TLS...
401nao_autenticadoChave de API ausente, malformada, desconhecida ou revogada.
402assinatura_inativaA assinatura da conta está suspensa ou cancelada. Regularize o pagamento no painel.
403sem_permissaoA chave é restrita a números e o recurso está fora do conjunto dela.
404nao_encontradoO recurso não existe ou é de outra conta (nunca confirmamos a existência).
409conflitoEstado incompatível: pausar campanha que não está em andamento, repassar lead já repassado...
409numero_indisponivelNúmero não oficial desconectado. Reconecte-o; o ZapHub não troca de número sozinho.
422regra_negocioRegra de negócio, ex.: conectar um número OFICIAL (use as credenciais da Meta no painel).
422campanha_exige_api_oficialCampanhas só saem por números OFICIAL.
422template_exige_api_oficialTemplates existem só em números OFICIAL.
422midia_url_invalidaURL da mídia sem HTTPS, com usuário/senha ou que aponta para host interno/IP privado.
422recurso_indisponivelO recurso não existe no tipo do número (ex.: enquete no oficial). fields.recurso diz qual; veja a matriz de recursos.
422idempotencia_corpo_diferenteA mesma Idempotency-Key já foi usada com outro corpo.
429limite_taxaMais de 600 requisições por minuto na chave. Aguarde Retry-After segundos.
502/503evolution_indisponivel · meta_erroO serviço de conexão do WhatsApp ou a Meta não respondeu. Tente novamente.
502código do canalReagir, editar, apagar, marcar como lida ou digitando: o WhatsApp recusou a operação e nada mudou.
500erro_internoErro inesperado do nosso lado. Informe o x-correlation-id da resposta ao suporte.

Falha do canal não é erro HTTP: se a Meta (ou o WhatsApp) recusar um envio já aceito, a resposta é 202 com status: "FALHOU" e erro: { codigo, codigoExterno, mensagem }, e o mesmo erro chega em mensagem.status.

Recursos por tipo de número

O contrato é o mesmo para os dois tipos de número; o que muda é o que cada um suporta. GET /v1/numeros/{id} traz a lista em recursos, e uma rota fora dela responde 422 recurso_indisponivel.

RecursorecursoNão oficialOficial
Texto envio.texto Sim Sim
Imagem, documento, áudio e vídeo envio.midia Sim Sim
Localização envio.localizacao Sim Sim
Contato (vCard) envio.contato Sim Sim
Figurinha envio.figurinha Sim Sim
Enquete envio.enquete Sim Não
Template envio.template Não Sim
Botões, lista e link (interativos) envio.interativo Não Sim
Responder citando (respondendoA) envio.citacao Sim Sim
Menções (grupos) envio.mencao Sim Não
Prévia de link envio.previaLink Sim Sim
Reagir operacao.reacao Sim Sim
Marcar como lida operacao.lida Sim Sim
"Digitando…" operacao.digitando Sim Sim
Editar (até 15 min) operacao.editar Sim Não
Apagar para todos (até 2 dias) operacao.apagar Sim Não
Verificar se o telefone tem WhatsApp contatos.verificar Sim Não
Foto e recado do contato contatos.info Sim Não
Bloquear e desbloquear contato contatos.bloquear Sim Não
Agenda de contatos numero.agenda Sim Não
Perfil do número (nome, foto, recado) numero.perfil Sim Sim
Privacidade numero.privacidade Sim Não
Configurações do número numero.configuracoes Sim Não
Arquivar, fixar e silenciar conversas conversa.organizar Sim Não
Sincronizar histórico historico.sincronizar Sim Não
Rejeitar chamadas chamada.rejeitar Sim Não
Grupos grupos Sim Não
Status (stories) numero.status Sim Não
Etiquetas (WhatsApp Business) etiquetas Sim Não
Comunidades comunidades Sim Não
Canais (newsletter) canais Sim Não

No não oficial, contato vai um por mensagem. Os votos de enquete chegam em mensagem.voto e em GET /v1/mensagens/{id}/votos. O indicador de digitação do oficial exige uma mensagem recebida do contato e some em até 25 segundos ou quando a resposta sai.

Referência dos endpoints

Gerada do OpenAPI 3.1 (/openapi.json), que sai dos mesmos schemas que a API usa para validar. Só os endpoints públicos /v1 estão aqui; nenhuma rota interna.

Números

Consultar números, conectar por QR Code ou código de pareamento e desconectar. Criar e remover números continua no painel (ocupa uma vaga do plano e guarda credenciais).

Exemplo: Listar, consultar e conectar

# Lista os números (tipo, telefone, status)
curl https://api.zaphub.wssengenharia.com.br/v1/numeros -H "Authorization: Bearer $ZAPHUB_KEY"

# Detalhe com indicadores
curl https://api.zaphub.wssengenharia.com.br/v1/numeros/clx0numero000000000000001 -H "Authorization: Bearer $ZAPHUB_KEY"

# QR Code (ou "-d '{\"telefone\":\"5511987654321\"}'" para o código de pareamento)
curl -X POST https://api.zaphub.wssengenharia.com.br/v1/numeros/clx0numero000000000000001/conectar -H "Authorization: Bearer $ZAPHUB_KEY"

# Desconecta
curl -X POST https://api.zaphub.wssengenharia.com.br/v1/numeros/clx0numero000000000000001/desconectar -H "Authorization: Bearer $ZAPHUB_KEY"
import { zaphub } from "./zaphub.js";

const { numeros } = await zaphub("GET", "/v1/numeros");
const numero = await zaphub("GET", "/v1/numeros/" + numeros[0].id);
console.log(numero.status, numero.indicadores.naFila);

const qr = await zaphub("POST", "/v1/numeros/" + numero.id + "/conectar");
const codigo = await zaphub("POST", "/v1/numeros/" + numero.id + "/conectar", { telefone: "5511987654321" });
await zaphub("POST", "/v1/numeros/" + numero.id + "/desconectar");
<?php
require 'zaphub.php';

[, $lista] = zaphub('GET', '/v1/numeros');
$id = $lista['numeros'][0]['id'];
[, $numero] = zaphub('GET', "/v1/numeros/$id");
[, $qr] = zaphub('POST', "/v1/numeros/$id/conectar");
[, $codigo] = zaphub('POST', "/v1/numeros/$id/conectar", ['telefone' => '5511987654321']);
zaphub('POST', "/v1/numeros/$id/desconectar");
get /v1/numeros

Lista os números

Números da conta (ou do conjunto permitido da chave). Token e credenciais da Meta nunca saem.

Resposta 200 · OK

Lista de números.

CampoTipoObrig.Descrição
numerosobject[]sim
numeros[].idstringsim
numeros[].tipoenumsim

Valores: OFICIAL, NAO_OFICIAL.

numeros[].apelidostringsim
numeros[].statusenumsim

Valores: DESCONECTADO, CONECTANDO, CONECTADO, ERRO, AGUARDANDO_WEBHOOK, ATIVO, RESTRITO, DESATIVADO, REMOCAO_PENDENTE.

numeros[].telefonestring | nullsim

E.164 sem +. Nulo até conectar (não oficial) ou validar (oficial).

numeros[].nomePerfilstring | nullsim
numeros[].ritmoPorMinutointeger | nullsim

Só NAO_OFICIAL: cadência de saída (mensagens/minuto).

numeros[].criadoEmstringsim
numeros[].ultimaAtividadeEmstring | nullsim
numeros[].oficialobject | nullsim
numeros[].oficial.qualityRatingstring | nullsim
numeros[].oficial.nomeVerificadostring | nullsim
numeros[].oficial.alertaPagamentobooleansim
numeros[].oficial.statusMotivostring | nullsim
numeros[].oficial.vazaoPorSegundointegersim
numeros[].naoOficialobject | nullsim
numeros[].naoOficial.conectadoEmstring | nullsim
numeros[].naoOficial.protegidoPorProxybooleansim
numeros[].indicadoresobjectnão

Só em GET /v1/numeros/{id}.

numeros[].indicadores.numeroIdstringsim
numeros[].indicadores.tipoenumsim

Valores: OFICIAL, NAO_OFICIAL.

numeros[].indicadores.geradoEmstringsim
numeros[].indicadores.iniciosConversaobjectsim
numeros[].indicadores.iniciosConversa.ultimaHoraintegersim
numeros[].indicadores.iniciosConversa.hojeintegersim
numeros[].indicadores.mensagensEnviadasobjectsim
numeros[].indicadores.mensagensEnviadas.ultimaHoraintegersim
numeros[].indicadores.mensagensEnviadas.hojeintegersim
numeros[].indicadores.desconexoesobjectsim
numeros[].indicadores.desconexoes.ultimas24hintegersim
numeros[].indicadores.desconexoes.ultimaEmstring | nullsim
numeros[].indicadores.ritmoPorMinutointeger | nullsim
numeros[].indicadores.naFilaintegersim
numeros[].recursosenum[]sim

O que o número suporta, conforme o tipo. Rota fora da lista responde 422 recurso_indisponivel.

Exemplo de resposta
{
  "numeros": [
    {
      "id": "clx0numero000000000000001",
      "tipo": "NAO_OFICIAL",
      "apelido": "Vendas",
      "status": "CONECTADO",
      "telefone": "5511987654321",
      "nomePerfil": "Loja Exemplo",
      "ritmoPorMinuto": 120,
      "criadoEm": "2026-10-01T12:00:00.000Z",
      "ultimaAtividadeEm": "2026-10-14T13:00:00.000Z",
      "oficial": null,
      "naoOficial": {
        "conectadoEm": "2026-10-01T12:05:00.000Z"
      }
    }
  ]
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
get /v1/numeros/{id}

Detalhe de um número

Tipo, telefone, status e indicadores informativos (inícios de conversa, mensagens enviadas, desconexões e fila). Os indicadores nunca bloqueiam nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador público do número.

Resposta 200 · OK

O número.

CampoTipoObrig.Descrição
idstringsim
tipoenumsim

Valores: OFICIAL, NAO_OFICIAL.

apelidostringsim
statusenumsim

Valores: DESCONECTADO, CONECTANDO, CONECTADO, ERRO, AGUARDANDO_WEBHOOK, ATIVO, RESTRITO, DESATIVADO, REMOCAO_PENDENTE.

telefonestring | nullsim

E.164 sem +. Nulo até conectar (não oficial) ou validar (oficial).

nomePerfilstring | nullsim
ritmoPorMinutointeger | nullsim

Só NAO_OFICIAL: cadência de saída (mensagens/minuto).

criadoEmstringsim
ultimaAtividadeEmstring | nullsim
oficialobject | nullsim
oficial.qualityRatingstring | nullsim
oficial.nomeVerificadostring | nullsim
oficial.alertaPagamentobooleansim
oficial.statusMotivostring | nullsim
oficial.vazaoPorSegundointegersim
naoOficialobject | nullsim
naoOficial.conectadoEmstring | nullsim
naoOficial.protegidoPorProxybooleansim
indicadoresobjectnão

Só em GET /v1/numeros/{id}.

indicadores.numeroIdstringsim
indicadores.tipoenumsim

Valores: OFICIAL, NAO_OFICIAL.

indicadores.geradoEmstringsim
indicadores.iniciosConversaobjectsim
indicadores.iniciosConversa.ultimaHoraintegersim
indicadores.iniciosConversa.hojeintegersim
indicadores.mensagensEnviadasobjectsim
indicadores.mensagensEnviadas.ultimaHoraintegersim
indicadores.mensagensEnviadas.hojeintegersim
indicadores.desconexoesobjectsim
indicadores.desconexoes.ultimas24hintegersim
indicadores.desconexoes.ultimaEmstring | nullsim
indicadores.ritmoPorMinutointeger | nullsim
indicadores.naFilaintegersim
recursosenum[]sim

O que o número suporta, conforme o tipo. Rota fora da lista responde 422 recurso_indisponivel.

Exemplo de resposta
{
  "id": "clx0numero000000000000001",
  "tipo": "NAO_OFICIAL",
  "apelido": "Vendas",
  "status": "CONECTADO",
  "telefone": "5511987654321",
  "nomePerfil": "Loja Exemplo",
  "ritmoPorMinuto": 120,
  "criadoEm": "2026-10-01T12:00:00.000Z",
  "ultimaAtividadeEm": "2026-10-14T13:00:00.000Z",
  "oficial": null,
  "naoOficial": {
    "conectadoEm": "2026-10-01T12:05:00.000Z"
  }
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/numeros/{id}/conectar

Conecta um número não oficial (QR ou código)

Somente NAO_OFICIAL; em número OFICIAL responde 422. Sem corpo devolve o QR atual (imagem base64 e texto). Com {"telefone": "..."} devolve o código de pareamento de 8 caracteres. Os QRs renovados chegam pelo evento numero.qrcode e o evento numero.status com CONECTADO confirma o pareamento.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador público do número.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
telefonestringnão

Com telefone, devolve o código de pareamento de 8 caracteres em vez do QR.

Exemplo de corpo
{
  "telefone": "5511987654321"
}

Resposta 200 · OK

QR ou código de pareamento.

CampoTipoObrig.Descrição
numeroobjectsim
numero.idstringsim
numero.tipoenumsim

Valores: OFICIAL, NAO_OFICIAL.

numero.apelidostringsim
numero.statusenumsim

Valores: DESCONECTADO, CONECTANDO, CONECTADO, ERRO, AGUARDANDO_WEBHOOK, ATIVO, RESTRITO, DESATIVADO, REMOCAO_PENDENTE.

numero.telefonestring | nullsim

E.164 sem +. Nulo até conectar (não oficial) ou validar (oficial).

numero.nomePerfilstring | nullsim
numero.ritmoPorMinutointeger | nullsim

Só NAO_OFICIAL: cadência de saída (mensagens/minuto).

numero.criadoEmstringsim
numero.ultimaAtividadeEmstring | nullsim
numero.oficialobject | nullsim
numero.oficial.qualityRatingstring | nullsim
numero.oficial.nomeVerificadostring | nullsim
numero.oficial.alertaPagamentobooleansim
numero.oficial.statusMotivostring | nullsim
numero.oficial.vazaoPorSegundointegersim
numero.naoOficialobject | nullsim
numero.naoOficial.conectadoEmstring | nullsim
numero.naoOficial.protegidoPorProxybooleansim
numero.indicadoresobjectnão

Só em GET /v1/numeros/{id}.

numero.indicadores.numeroIdstringsim
numero.indicadores.tipoenumsim

Valores: OFICIAL, NAO_OFICIAL.

numero.indicadores.geradoEmstringsim
numero.indicadores.iniciosConversaobjectsim
numero.indicadores.iniciosConversa.ultimaHoraintegersim
numero.indicadores.iniciosConversa.hojeintegersim
numero.indicadores.mensagensEnviadasobjectsim
numero.indicadores.mensagensEnviadas.ultimaHoraintegersim
numero.indicadores.mensagensEnviadas.hojeintegersim
numero.indicadores.desconexoesobjectsim
numero.indicadores.desconexoes.ultimas24hintegersim
numero.indicadores.desconexoes.ultimaEmstring | nullsim
numero.indicadores.ritmoPorMinutointeger | nullsim
numero.indicadores.naFilaintegersim
numero.recursosenum[]sim

O que o número suporta, conforme o tipo. Rota fora da lista responde 422 recurso_indisponivel.

modoenumsim

Valores: QR, CODIGO.

qrcodestring | nullsim

Imagem do QR (data URL PNG). Pode ser nula se o primeiro QR ainda não foi gerado: ele chega pelo evento numero.qrcode.

codigostring | nullsim

Texto do QR, para renderizar no seu sistema.

codigoPareamentostring | nullsim
expiraEmstringsim

Depois disso o QR deixa de ser renovado; chame conectar de novo.

Exemplo de resposta
{
  "numero": {
    "id": "texto",
    "tipo": "OFICIAL",
    "apelido": "texto",
    "status": "DESCONECTADO",
    "telefone": "texto",
    "nomePerfil": "texto",
    "ritmoPorMinuto": 0,
    "criadoEm": "texto",
    "ultimaAtividadeEm": "texto",
    "oficial": {
      "qualityRating": "texto",
      "nomeVerificado": "texto",
      "alertaPagamento": false,
      "statusMotivo": "texto",
      "vazaoPorSegundo": 0
    },
    "naoOficial": {
      "conectadoEm": "texto",
      "protegidoPorProxy": false
    },
    "indicadores": {
      "numeroId": "texto",
      "tipo": "OFICIAL",
      "geradoEm": "texto",
      "iniciosConversa": {
        "ultimaHora": 0,
        "hoje": 0
      },
      "mensagensEnviadas": {
        "ultimaHora": 0,
        "hoje": 0
      },
      "desconexoes": {
        "ultimas24h": 0,
        "ultimaEm": "texto"
      },
      "ritmoPorMinuto": 0,
      "naFila": 0
    },
    "recursos": [
      "envio.texto"
    ]
  },
  "modo": "QR",
  "qrcode": "texto",
  "codigo": "texto",
  "codigoPareamento": "texto",
  "expiraEm": "texto"
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/numeros/{id}/desconectar

Desconecta um número não oficial

Faz o logout da sessão do WhatsApp mantendo o número e a vaga do plano.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador público do número.

Resposta 200 · OK

O número desconectado.

CampoTipoObrig.Descrição
idstringsim
tipoenumsim

Valores: OFICIAL, NAO_OFICIAL.

apelidostringsim
statusenumsim

Valores: DESCONECTADO, CONECTANDO, CONECTADO, ERRO, AGUARDANDO_WEBHOOK, ATIVO, RESTRITO, DESATIVADO, REMOCAO_PENDENTE.

telefonestring | nullsim

E.164 sem +. Nulo até conectar (não oficial) ou validar (oficial).

nomePerfilstring | nullsim
ritmoPorMinutointeger | nullsim

Só NAO_OFICIAL: cadência de saída (mensagens/minuto).

criadoEmstringsim
ultimaAtividadeEmstring | nullsim
oficialobject | nullsim
oficial.qualityRatingstring | nullsim
oficial.nomeVerificadostring | nullsim
oficial.alertaPagamentobooleansim
oficial.statusMotivostring | nullsim
oficial.vazaoPorSegundointegersim
naoOficialobject | nullsim
naoOficial.conectadoEmstring | nullsim
naoOficial.protegidoPorProxybooleansim
indicadoresobjectnão

Só em GET /v1/numeros/{id}.

indicadores.numeroIdstringsim
indicadores.tipoenumsim

Valores: OFICIAL, NAO_OFICIAL.

indicadores.geradoEmstringsim
indicadores.iniciosConversaobjectsim
indicadores.iniciosConversa.ultimaHoraintegersim
indicadores.iniciosConversa.hojeintegersim
indicadores.mensagensEnviadasobjectsim
indicadores.mensagensEnviadas.ultimaHoraintegersim
indicadores.mensagensEnviadas.hojeintegersim
indicadores.desconexoesobjectsim
indicadores.desconexoes.ultimas24hintegersim
indicadores.desconexoes.ultimaEmstring | nullsim
indicadores.ritmoPorMinutointeger | nullsim
indicadores.naFilaintegersim
recursosenum[]sim

O que o número suporta, conforme o tipo. Rota fora da lista responde 422 recurso_indisponivel.

Exemplo de resposta
{
  "id": "texto",
  "tipo": "OFICIAL",
  "apelido": "texto",
  "status": "DESCONECTADO",
  "telefone": "texto",
  "nomePerfil": "texto",
  "ritmoPorMinuto": 0,
  "criadoEm": "texto",
  "ultimaAtividadeEm": "texto",
  "oficial": {
    "qualityRating": "texto",
    "nomeVerificado": "texto",
    "alertaPagamento": false,
    "statusMotivo": "texto",
    "vazaoPorSegundo": 0
  },
  "naoOficial": {
    "conectadoEm": "texto",
    "protegidoPorProxy": false
  },
  "indicadores": {
    "numeroId": "texto",
    "tipo": "OFICIAL",
    "geradoEm": "texto",
    "iniciosConversa": {
      "ultimaHora": 0,
      "hoje": 0
    },
    "mensagensEnviadas": {
      "ultimaHora": 0,
      "hoje": 0
    },
    "desconexoes": {
      "ultimas24h": 0,
      "ultimaEm": "texto"
    },
    "ritmoPorMinuto": 0,
    "naFila": 0
  },
  "recursos": [
    "envio.texto"
  ]
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.

Mensagens

Envio individual de texto, mídia (por URL HTTPS) e template, e consulta de mensagens e conversas.

Exemplo: Enviar texto e mídia

# Texto
curl -X POST https://api.zaphub.wssengenharia.com.br/v1/mensagens/texto \
  -H "Authorization: Bearer $ZAPHUB_KEY" -H "Idempotency-Key: pedido-1042" -H "Content-Type: application/json" \
  -d '{"numeroId":"clx0numero000000000000001","para":"5511987654321","texto":"Olá!"}'

# Mídia por URL HTTPS pública (imagem | documento | audio | video)
curl -X POST https://api.zaphub.wssengenharia.com.br/v1/mensagens/midia \
  -H "Authorization: Bearer $ZAPHUB_KEY" -H "Content-Type: application/json" \
  -d '{"numeroId":"clx0numero000000000000001","para":"5511987654321","tipo":"documento",
       "url":"https://cdn.exemplo.com.br/proposta.pdf","legenda":"Segue a proposta","nomeArquivo":"proposta.pdf"}'
import { zaphub } from "./zaphub.js";

const numeroId = "clx0numero000000000000001";
await zaphub("POST", "/v1/mensagens/texto", { numeroId, para: "5511987654321", texto: "Olá!" }, { "Idempotency-Key": "pedido-1042" });

await zaphub("POST", "/v1/mensagens/midia", {
  numeroId,
  para: "5511987654321",
  tipo: "documento",
  url: "https://cdn.exemplo.com.br/proposta.pdf",
  legenda: "Segue a proposta",
  nomeArquivo: "proposta.pdf",
});
<?php
require 'zaphub.php';

$numeroId = 'clx0numero000000000000001';
zaphub('POST', '/v1/mensagens/texto', ['numeroId' => $numeroId, 'para' => '5511987654321', 'texto' => 'Olá!'], ['Idempotency-Key: pedido-1042']);

zaphub('POST', '/v1/mensagens/midia', [
    'numeroId' => $numeroId,
    'para' => '5511987654321',
    'tipo' => 'documento',
    'url' => 'https://cdn.exemplo.com.br/proposta.pdf',
    'legenda' => 'Segue a proposta',
    'nomeArquivo' => 'proposta.pdf',
]);

Exemplo: Enviar template (número oficial) e tratar a recusa da Meta

curl -X POST https://api.zaphub.wssengenharia.com.br/v1/mensagens/template \
  -H "Authorization: Bearer $ZAPHUB_KEY" -H "Content-Type: application/json" \
  -d '{"numeroId":"clx0oficial00000000000001","para":"5511987654321",
       "templateId":"clx0template0000000000001","variaveis":["Ana","COD-1042"]}'

# 202 {"status":"ENVIADA", ..., "custoEstimado":{"categoria":"MARKETING","centavos":34,"estimado":true}}
# Se a Meta recusar: 202 {"status":"FALHOU","erro":{"codigo":"janela_fechada","codigoExterno":"131047","mensagem":"..."}}
import { zaphub } from "./zaphub.js";

const r = await zaphub("POST", "/v1/mensagens/template", {
  numeroId: "clx0oficial00000000000001",
  para: "5511987654321",
  templateId: "clx0template0000000000001",
  variaveis: ["Ana", "COD-1042"],
});
if (r.status === "FALHOU") {
  // A Meta recusou: erro.codigoExterno é o código dela (ex.: "131047" = fora da janela de 24h)
  console.error(r.erro.codigo, r.erro.codigoExterno, r.erro.mensagem);
}
<?php
require 'zaphub.php';

[, $r] = zaphub('POST', '/v1/mensagens/template', [
    'numeroId' => 'clx0oficial00000000000001',
    'para' => '5511987654321',
    'templateId' => 'clx0template0000000000001',
    'variaveis' => ['Ana', 'COD-1042'],
]);
if ($r['status'] === 'FALHOU') {
    // A Meta recusou: erro.codigoExterno é o código dela (ex.: 131047 = fora da janela de 24h)
    error_log($r['erro']['codigo'] . ' ' . $r['erro']['codigoExterno'] . ' ' . $r['erro']['mensagem']);
}

Exemplo: Consultar uma mensagem e o histórico de uma conversa

curl https://api.zaphub.wssengenharia.com.br/v1/mensagens/clx0mensagem0000000000001 -H "Authorization: Bearer $ZAPHUB_KEY"

# Mensagens do contato neste número, da mais nova para a mais antiga (cursor = proximoCursor)
curl "https://api.zaphub.wssengenharia.com.br/v1/conversas?numeroId=clx0numero000000000000001&telefone=5511987654321&limite=40" \
  -H "Authorization: Bearer $ZAPHUB_KEY"
import { zaphub } from "./zaphub.js";

const msg = await zaphub("GET", "/v1/mensagens/clx0mensagem0000000000001");

let cursor = "";
do {
  const q = new URLSearchParams({ numeroId: "clx0numero000000000000001", telefone: "5511987654321", limite: "40" });
  if (cursor) q.set("cursor", cursor);
  const pagina = await zaphub("GET", "/v1/conversas?" + q);
  for (const m of pagina.mensagens.itens) console.log(m.direcao, m.texto);
  cursor = pagina.mensagens.proximoCursor;
} while (cursor);
<?php
require 'zaphub.php';

[, $msg] = zaphub('GET', '/v1/mensagens/clx0mensagem0000000000001');

$cursor = null;
do {
    $q = ['numeroId' => 'clx0numero000000000000001', 'telefone' => '5511987654321', 'limite' => 40];
    if ($cursor) $q['cursor'] = $cursor;
    [, $pagina] = zaphub('GET', '/v1/conversas?' . http_build_query($q));
    foreach ($pagina['mensagens']['itens'] as $m) echo $m['direcao'] . ': ' . $m['texto'] . PHP_EOL;
    $cursor = $pagina['mensagens']['proximoCursor'];
} while ($cursor);
post /v1/mensagens/texto

Envia texto

Responde 202: a mensagem foi aceita e o resultado final chega no evento mensagem.status. Em número OFICIAL tudo é encaminhado à Meta sem bloqueio do ZapHub; se a Meta recusar (por exemplo, texto livre fora da janela de 24h), a resposta traz status: FALHOU e erro com o código e a mensagem da Meta. Em NAO_OFICIAL não há bloqueio por volume: acima do ritmo do número a mensagem fica enfileirada e sai logo em seguida.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros). Tamanho: 1 a 64.

parastringnão

Telefone do contato com DDI e DDD. Aceita máscara (+55 (11) 98765-4321); sem DDI e com 10 ou 11 dígitos assume o Brasil (55). Normalizado para E.164 sem +. Tamanho: 8 a 30.

textostringsim

Tamanho: 1 a 4096.

previaLinkbooleannão

Pede a prévia do primeiro link do texto.

respondendoAstringnão

id de uma mensagem da mesma conversa: o envio sai citando ela. Tamanho: 1 a 64.

mencoesstring[]não

Telefones mencionados (só em grupos; em conversa individual é aceito e ignorado). Só NAO_OFICIAL. No máximo 50 itens.

grupoIdstringnão

Envia ao grupo (id de GET /v1/numeros/{id}/grupos) em vez de para. Só NAO_OFICIAL. Tamanho: 1 a 64.

mencionarTodosbooleannão

Só em grupo: menciona todos os participantes.

Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001",
  "para": "5511987654321",
  "texto": "Olá! Seu pedido saiu para entrega."
}

Resposta 202 · Aceito

Mensagem aceita.

CampoTipoObrig.Descrição
idstringsim

Identificador da mensagem (GET /v1/mensagens/{id}). O resultado final chega em mensagem.status.

tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

tipoEnvioenumsim

Valores: PRIMEIRO_CONTATO, PRIMEIRA_RESPOSTA, CONVERSA, RETOMADA, CONTATO_LEAD, AUTO_RESPOSTA, CAMPANHA, GRUPO.

statusenumsim

Valores: ENFILEIRADA, ENVIADA, ENTREGUE, LIDA, FALHOU, RECEBIDA.

enfileiradabooleansim

true = na fila de cadência do número (não oficial); sai logo em seguida.

custoEvitadoobject | nullsim

Só números NAO_OFICIAL: quanto custaria a mesma mensagem na API Oficial.

custoEvitado.marketingCentavosintegersim
custoEvitado.utilityCentavosintegersim
contatoOptoutbooleansim

O contato pediu para sair; o envio não é bloqueado, só sinalizado.

janelaAbertabooleansim

Janela de 24h aberta (informativo).

erroobjectnão

Só com status: FALHOU: o erro do canal (na Meta, com o código dela).

erro.codigostringsim
erro.codigoExternostring | nullsim
erro.mensagemstringsim
custoEstimadoobject | nullnão

Só em templates: estimativa pela tabela pública.

custoEstimado.categoriaenumsim

Valores: MARKETING, UTILITY, AUTHENTICATION.

custoEstimado.centavosintegersim

Estimativa do que a Meta cobra por esta mensagem, em centavos de BRL.

custoEstimado.precoUsdstringsim
custoEstimado.cambiostringsim
custoEstimado.tabelaVersaointegersim
custoEstimado.estimadoenumsim

Valores: true.

Exemplo de resposta
{
  "id": "clx0mensagem0000000000001",
  "tipoNumero": "NAO_OFICIAL",
  "tipoEnvio": "PRIMEIRO_CONTATO",
  "status": "ENVIADA",
  "enfileirada": false,
  "custoEvitado": {
    "marketingCentavos": 34,
    "utilityCentavos": 7
  },
  "contatoOptout": false,
  "janelaAberta": false
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/mensagens/midia

Envia imagem, documento, áudio ou vídeo

Responde 202: a mensagem foi aceita e o resultado final chega no evento mensagem.status. Em número OFICIAL tudo é encaminhado à Meta sem bloqueio do ZapHub; se a Meta recusar (por exemplo, texto livre fora da janela de 24h), a resposta traz status: FALHOU e erro com o código e a mensagem da Meta. Em NAO_OFICIAL não há bloqueio por volume: acima do ritmo do número a mensagem fica enfileirada e sai logo em seguida.

A mídia vai por URL HTTPS pública (sem upload). URLs http, com usuário/senha, de hosts internos ou que resolvam para IP privado/loopback são recusadas com 422 midia_url_invalida.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros). Tamanho: 1 a 64.

parastringnão

Telefone do contato com DDI e DDD. Aceita máscara (+55 (11) 98765-4321); sem DDI e com 10 ou 11 dígitos assume o Brasil (55). Normalizado para E.164 sem +. Tamanho: 8 a 30.

tipoenumsim

Valores: imagem, documento, audio, video.

urlstringnão

URL HTTPS pública. http, hosts internos e IPs privados/loopback são recusados (422). Tamanho: 1 a 2048.

base64stringnão

Conteúdo em base64 (ou data URL). Alternativa a url. Tamanho: 0 a 23000000.

mimeTypestringnão

Obrigatório com base64 (ex.: application/pdf).

legendastringnão

Tamanho: 0 a 1024.

nomeArquivostringnão

Nome exibido para documentos. Tamanho: 1 a 255.

respondendoAstringnão

id de uma mensagem da mesma conversa: o envio sai citando ela. Tamanho: 1 a 64.

mencoesstring[]não

Telefones mencionados (só em grupos; em conversa individual é aceito e ignorado). Só NAO_OFICIAL. No máximo 50 itens.

grupoIdstringnão

Envia ao grupo (id de GET /v1/numeros/{id}/grupos) em vez de para. Só NAO_OFICIAL. Tamanho: 1 a 64.

mencionarTodosbooleannão

Só em grupo: menciona todos os participantes.

Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001",
  "para": "5511987654321",
  "tipo": "documento",
  "url": "https://cdn.exemplo.com.br/proposta.pdf",
  "legenda": "Segue a proposta",
  "nomeArquivo": "proposta.pdf"
}

Resposta 202 · Aceito

Mensagem aceita.

CampoTipoObrig.Descrição
idstringsim

Identificador da mensagem (GET /v1/mensagens/{id}). O resultado final chega em mensagem.status.

tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

tipoEnvioenumsim

Valores: PRIMEIRO_CONTATO, PRIMEIRA_RESPOSTA, CONVERSA, RETOMADA, CONTATO_LEAD, AUTO_RESPOSTA, CAMPANHA, GRUPO.

statusenumsim

Valores: ENFILEIRADA, ENVIADA, ENTREGUE, LIDA, FALHOU, RECEBIDA.

enfileiradabooleansim

true = na fila de cadência do número (não oficial); sai logo em seguida.

custoEvitadoobject | nullsim

Só números NAO_OFICIAL: quanto custaria a mesma mensagem na API Oficial.

custoEvitado.marketingCentavosintegersim
custoEvitado.utilityCentavosintegersim
contatoOptoutbooleansim

O contato pediu para sair; o envio não é bloqueado, só sinalizado.

janelaAbertabooleansim

Janela de 24h aberta (informativo).

erroobjectnão

Só com status: FALHOU: o erro do canal (na Meta, com o código dela).

erro.codigostringsim
erro.codigoExternostring | nullsim
erro.mensagemstringsim
custoEstimadoobject | nullnão

Só em templates: estimativa pela tabela pública.

custoEstimado.categoriaenumsim

Valores: MARKETING, UTILITY, AUTHENTICATION.

custoEstimado.centavosintegersim

Estimativa do que a Meta cobra por esta mensagem, em centavos de BRL.

custoEstimado.precoUsdstringsim
custoEstimado.cambiostringsim
custoEstimado.tabelaVersaointegersim
custoEstimado.estimadoenumsim

Valores: true.

Exemplo de resposta
{
  "id": "clx0mensagem0000000000001",
  "tipoNumero": "NAO_OFICIAL",
  "tipoEnvio": "PRIMEIRO_CONTATO",
  "status": "ENVIADA",
  "enfileirada": false,
  "custoEvitado": {
    "marketingCentavos": 34,
    "utilityCentavos": 7
  },
  "contatoOptout": false,
  "janelaAberta": false
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/mensagens/localizacao

Envia uma localização (pin no mapa)

Responde 202: a mensagem foi aceita e o resultado final chega no evento mensagem.status. Em número OFICIAL tudo é encaminhado à Meta sem bloqueio do ZapHub; se a Meta recusar (por exemplo, texto livre fora da janela de 24h), a resposta traz status: FALHOU e erro com o código e a mensagem da Meta. Em NAO_OFICIAL não há bloqueio por volume: acima do ritmo do número a mensagem fica enfileirada e sai logo em seguida.

Aceita respondendoA (cita uma mensagem da mesma conversa: 404 se ela não é da conta, 422 se é de outra conversa) e mencoes (só não oficial; efeito em grupos). Para grupo, envie grupoId no lugar de para (com mencionarTodos, se quiser): sem opt-out individual, tipo de envio GRUPO, fora da economia e na cadência do número.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros). Tamanho: 1 a 64.

parastringnão

Telefone do contato com DDI e DDD. Aceita máscara (+55 (11) 98765-4321); sem DDI e com 10 ou 11 dígitos assume o Brasil (55). Normalizado para E.164 sem +. Tamanho: 8 a 30.

latitudenumbersim

Intervalo: -90 a 90.

longitudenumbersim

Intervalo: -180 a 180.

nomestringnão

Tamanho: 0 a 256.

enderecostringnão

Tamanho: 0 a 512.

respondendoAstringnão

id de uma mensagem da mesma conversa: o envio sai citando ela. Tamanho: 1 a 64.

mencoesstring[]não

Telefones mencionados (só em grupos; em conversa individual é aceito e ignorado). Só NAO_OFICIAL. No máximo 50 itens.

grupoIdstringnão

Envia ao grupo (id de GET /v1/numeros/{id}/grupos) em vez de para. Só NAO_OFICIAL. Tamanho: 1 a 64.

mencionarTodosbooleannão

Só em grupo: menciona todos os participantes.

Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001",
  "para": "5511987654321",
  "latitude": -20.3155,
  "longitude": -40.3128,
  "nome": "Loja Centro",
  "endereco": "Av. Princesa Isabel, 100 - Vitória/ES"
}

Resposta 202 · Aceito

Mensagem aceita.

CampoTipoObrig.Descrição
idstringsim

Identificador da mensagem (GET /v1/mensagens/{id}). O resultado final chega em mensagem.status.

tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

tipoEnvioenumsim

Valores: PRIMEIRO_CONTATO, PRIMEIRA_RESPOSTA, CONVERSA, RETOMADA, CONTATO_LEAD, AUTO_RESPOSTA, CAMPANHA, GRUPO.

statusenumsim

Valores: ENFILEIRADA, ENVIADA, ENTREGUE, LIDA, FALHOU, RECEBIDA.

enfileiradabooleansim

true = na fila de cadência do número (não oficial); sai logo em seguida.

custoEvitadoobject | nullsim

Só números NAO_OFICIAL: quanto custaria a mesma mensagem na API Oficial.

custoEvitado.marketingCentavosintegersim
custoEvitado.utilityCentavosintegersim
contatoOptoutbooleansim

O contato pediu para sair; o envio não é bloqueado, só sinalizado.

janelaAbertabooleansim

Janela de 24h aberta (informativo).

erroobjectnão

Só com status: FALHOU: o erro do canal (na Meta, com o código dela).

erro.codigostringsim
erro.codigoExternostring | nullsim
erro.mensagemstringsim
custoEstimadoobject | nullnão

Só em templates: estimativa pela tabela pública.

custoEstimado.categoriaenumsim

Valores: MARKETING, UTILITY, AUTHENTICATION.

custoEstimado.centavosintegersim

Estimativa do que a Meta cobra por esta mensagem, em centavos de BRL.

custoEstimado.precoUsdstringsim
custoEstimado.cambiostringsim
custoEstimado.tabelaVersaointegersim
custoEstimado.estimadoenumsim

Valores: true.

Exemplo de resposta
{
  "id": "clx0mensagem0000000000001",
  "tipoNumero": "NAO_OFICIAL",
  "tipoEnvio": "PRIMEIRO_CONTATO",
  "status": "ENVIADA",
  "enfileirada": false,
  "custoEvitado": {
    "marketingCentavos": 34,
    "utilityCentavos": 7
  },
  "contatoOptout": false,
  "janelaAberta": false
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/mensagens/contato

Compartilha contatos (vCard)

Responde 202: a mensagem foi aceita e o resultado final chega no evento mensagem.status. Em número OFICIAL tudo é encaminhado à Meta sem bloqueio do ZapHub; se a Meta recusar (por exemplo, texto livre fora da janela de 24h), a resposta traz status: FALHOU e erro com o código e a mensagem da Meta. Em NAO_OFICIAL não há bloqueio por volume: acima do ritmo do número a mensagem fica enfileirada e sai logo em seguida.

Até 10 contatos no oficial. Números não oficiais enviam um contato por mensagem, com o primeiro telefone dele (limite do Evolution Go). Aceita respondendoA (cita uma mensagem da mesma conversa: 404 se ela não é da conta, 422 se é de outra conversa) e mencoes (só não oficial; efeito em grupos). Para grupo, envie grupoId no lugar de para (com mencionarTodos, se quiser): sem opt-out individual, tipo de envio GRUPO, fora da economia e na cadência do número.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros). Tamanho: 1 a 64.

parastringnão

Telefone do contato com DDI e DDD. Aceita máscara (+55 (11) 98765-4321); sem DDI e com 10 ou 11 dígitos assume o Brasil (55). Normalizado para E.164 sem +. Tamanho: 8 a 30.

contatosobject[]sim

No máximo 10 itens.

contatos[].nomestringsim

Tamanho: 1 a 256.

contatos[].telefonesstring[]sim

No máximo 5 itens.

contatos[].empresastringnão

Tamanho: 0 a 256.

contatos[].emailstringnão

Tamanho: 0 a 256.

respondendoAstringnão

id de uma mensagem da mesma conversa: o envio sai citando ela. Tamanho: 1 a 64.

mencoesstring[]não

Telefones mencionados (só em grupos; em conversa individual é aceito e ignorado). Só NAO_OFICIAL. No máximo 50 itens.

grupoIdstringnão

Envia ao grupo (id de GET /v1/numeros/{id}/grupos) em vez de para. Só NAO_OFICIAL. Tamanho: 1 a 64.

mencionarTodosbooleannão

Só em grupo: menciona todos os participantes.

Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001",
  "para": "5511987654321",
  "contatos": [
    {
      "nome": "Suporte Loja Centro",
      "telefones": [
        "5527999990000"
      ],
      "empresa": "Loja Centro"
    }
  ]
}

Resposta 202 · Aceito

Mensagem aceita.

CampoTipoObrig.Descrição
idstringsim

Identificador da mensagem (GET /v1/mensagens/{id}). O resultado final chega em mensagem.status.

tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

tipoEnvioenumsim

Valores: PRIMEIRO_CONTATO, PRIMEIRA_RESPOSTA, CONVERSA, RETOMADA, CONTATO_LEAD, AUTO_RESPOSTA, CAMPANHA, GRUPO.

statusenumsim

Valores: ENFILEIRADA, ENVIADA, ENTREGUE, LIDA, FALHOU, RECEBIDA.

enfileiradabooleansim

true = na fila de cadência do número (não oficial); sai logo em seguida.

custoEvitadoobject | nullsim

Só números NAO_OFICIAL: quanto custaria a mesma mensagem na API Oficial.

custoEvitado.marketingCentavosintegersim
custoEvitado.utilityCentavosintegersim
contatoOptoutbooleansim

O contato pediu para sair; o envio não é bloqueado, só sinalizado.

janelaAbertabooleansim

Janela de 24h aberta (informativo).

erroobjectnão

Só com status: FALHOU: o erro do canal (na Meta, com o código dela).

erro.codigostringsim
erro.codigoExternostring | nullsim
erro.mensagemstringsim
custoEstimadoobject | nullnão

Só em templates: estimativa pela tabela pública.

custoEstimado.categoriaenumsim

Valores: MARKETING, UTILITY, AUTHENTICATION.

custoEstimado.centavosintegersim

Estimativa do que a Meta cobra por esta mensagem, em centavos de BRL.

custoEstimado.precoUsdstringsim
custoEstimado.cambiostringsim
custoEstimado.tabelaVersaointegersim
custoEstimado.estimadoenumsim

Valores: true.

Exemplo de resposta
{
  "id": "clx0mensagem0000000000001",
  "tipoNumero": "NAO_OFICIAL",
  "tipoEnvio": "PRIMEIRO_CONTATO",
  "status": "ENVIADA",
  "enfileirada": false,
  "custoEvitado": {
    "marketingCentavos": 34,
    "utilityCentavos": 7
  },
  "contatoOptout": false,
  "janelaAberta": false
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/mensagens/figurinha

Envia uma figurinha

Responde 202: a mensagem foi aceita e o resultado final chega no evento mensagem.status. Em número OFICIAL tudo é encaminhado à Meta sem bloqueio do ZapHub; se a Meta recusar (por exemplo, texto livre fora da janela de 24h), a resposta traz status: FALHOU e erro com o código e a mensagem da Meta. Em NAO_OFICIAL não há bloqueio por volume: acima do ritmo do número a mensagem fica enfileirada e sai logo em seguida.

WebP por URL HTTPS pública (as mesmas regras da mídia). No não oficial, outras imagens são convertidas para WebP pelo Evolution. Aceita respondendoA (cita uma mensagem da mesma conversa: 404 se ela não é da conta, 422 se é de outra conversa) e mencoes (só não oficial; efeito em grupos). Para grupo, envie grupoId no lugar de para (com mencionarTodos, se quiser): sem opt-out individual, tipo de envio GRUPO, fora da economia e na cadência do número.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros). Tamanho: 1 a 64.

parastringnão

Telefone do contato com DDI e DDD. Aceita máscara (+55 (11) 98765-4321); sem DDI e com 10 ou 11 dígitos assume o Brasil (55). Normalizado para E.164 sem +. Tamanho: 8 a 30.

urlstringnão

URL HTTPS pública de um WebP (até 500 KB; animada até 1 MB). Tamanho: 1 a 2048.

base64stringnão

Conteúdo em base64 (ou data URL). Alternativa a url. Tamanho: 0 a 23000000.

mimeTypestringnão

Obrigatório com base64 (ex.: application/pdf).

respondendoAstringnão

id de uma mensagem da mesma conversa: o envio sai citando ela. Tamanho: 1 a 64.

mencoesstring[]não

Telefones mencionados (só em grupos; em conversa individual é aceito e ignorado). Só NAO_OFICIAL. No máximo 50 itens.

grupoIdstringnão

Envia ao grupo (id de GET /v1/numeros/{id}/grupos) em vez de para. Só NAO_OFICIAL. Tamanho: 1 a 64.

mencionarTodosbooleannão

Só em grupo: menciona todos os participantes.

Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001",
  "para": "5511987654321",
  "url": "https://cdn.exemplo.com.br/obrigado.webp"
}

Resposta 202 · Aceito

Mensagem aceita.

CampoTipoObrig.Descrição
idstringsim

Identificador da mensagem (GET /v1/mensagens/{id}). O resultado final chega em mensagem.status.

tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

tipoEnvioenumsim

Valores: PRIMEIRO_CONTATO, PRIMEIRA_RESPOSTA, CONVERSA, RETOMADA, CONTATO_LEAD, AUTO_RESPOSTA, CAMPANHA, GRUPO.

statusenumsim

Valores: ENFILEIRADA, ENVIADA, ENTREGUE, LIDA, FALHOU, RECEBIDA.

enfileiradabooleansim

true = na fila de cadência do número (não oficial); sai logo em seguida.

custoEvitadoobject | nullsim

Só números NAO_OFICIAL: quanto custaria a mesma mensagem na API Oficial.

custoEvitado.marketingCentavosintegersim
custoEvitado.utilityCentavosintegersim
contatoOptoutbooleansim

O contato pediu para sair; o envio não é bloqueado, só sinalizado.

janelaAbertabooleansim

Janela de 24h aberta (informativo).

erroobjectnão

Só com status: FALHOU: o erro do canal (na Meta, com o código dela).

erro.codigostringsim
erro.codigoExternostring | nullsim
erro.mensagemstringsim
custoEstimadoobject | nullnão

Só em templates: estimativa pela tabela pública.

custoEstimado.categoriaenumsim

Valores: MARKETING, UTILITY, AUTHENTICATION.

custoEstimado.centavosintegersim

Estimativa do que a Meta cobra por esta mensagem, em centavos de BRL.

custoEstimado.precoUsdstringsim
custoEstimado.cambiostringsim
custoEstimado.tabelaVersaointegersim
custoEstimado.estimadoenumsim

Valores: true.

Exemplo de resposta
{
  "id": "clx0mensagem0000000000001",
  "tipoNumero": "NAO_OFICIAL",
  "tipoEnvio": "PRIMEIRO_CONTATO",
  "status": "ENVIADA",
  "enfileirada": false,
  "custoEvitado": {
    "marketingCentavos": 34,
    "utilityCentavos": 7
  },
  "contatoOptout": false,
  "janelaAberta": false
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/mensagens/interativa

Envia botões, lista ou link (número oficial)

Responde 202: a mensagem foi aceita e o resultado final chega no evento mensagem.status. Em número OFICIAL tudo é encaminhado à Meta sem bloqueio do ZapHub; se a Meta recusar (por exemplo, texto livre fora da janela de 24h), a resposta traz status: FALHOU e erro com o código e a mensagem da Meta. Em NAO_OFICIAL não há bloqueio por volume: acima do ritmo do número a mensagem fica enfileirada e sai logo em seguida.

Somente números OFICIAL (no não oficial, 422 recurso_indisponivel). botoes: até 3, título até 20 caracteres. lista: até 10 itens no total. link: botão com URL HTTPS. Segue a janela de 24h da Meta: fora dela, status: FALHOU com o erro da Meta. A escolha do contato chega em mensagem.recebida com interacao (tipo, id, titulo).

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Número OFICIAL. Tamanho: 1 a 64.

parastringsim

Telefone do contato com DDI e DDD. Aceita máscara (+55 (11) 98765-4321); sem DDI e com 10 ou 11 dígitos assume o Brasil (55). Normalizado para E.164 sem +. Tamanho: 8 a 30.

tipoenumsim

Valores: botoes, lista, link.

corpostringsim

Tamanho: 1 a 1024.

cabecalhostringnão

Tamanho: 0 a 60.

rodapestringnão

Tamanho: 0 a 60.

botoesobject[]não

Só em botoes. No máximo 3 itens.

botoes[].idstringsim

Tamanho: 1 a 256.

botoes[].titulostringsim

Tamanho: 1 a 20.

textoBotaostringnão

Em lista e link: o texto do botão. Tamanho: 1 a 20.

secoesobject[]não

Só em lista: até 10 itens no total. No máximo 10 itens.

secoes[].titulostringnão

Tamanho: 0 a 24.

secoes[].itensobject[]sim
secoes[].itens[].idstringsim

Tamanho: 1 a 200.

secoes[].itens[].titulostringsim

Tamanho: 1 a 24.

secoes[].itens[].descricaostringnão

Tamanho: 0 a 72.

urlstringnão

Só em link: URL HTTPS do botão. Tamanho: 0 a 2048.

respondendoAstringnão

Tamanho: 1 a 64.

Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001",
  "para": "5511987654321",
  "tipo": "botoes",
  "corpo": "Confirma o agendamento de amanhã às 10h?",
  "botoes": [
    {
      "id": "conf_sim",
      "titulo": "Sim"
    },
    {
      "id": "conf_nao",
      "titulo": "Não"
    }
  ]
}

Resposta 202 · Aceito

Mensagem aceita.

CampoTipoObrig.Descrição
idstringsim

Identificador da mensagem (GET /v1/mensagens/{id}). O resultado final chega em mensagem.status.

tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

tipoEnvioenumsim

Valores: PRIMEIRO_CONTATO, PRIMEIRA_RESPOSTA, CONVERSA, RETOMADA, CONTATO_LEAD, AUTO_RESPOSTA, CAMPANHA, GRUPO.

statusenumsim

Valores: ENFILEIRADA, ENVIADA, ENTREGUE, LIDA, FALHOU, RECEBIDA.

enfileiradabooleansim

true = na fila de cadência do número (não oficial); sai logo em seguida.

custoEvitadoobject | nullsim

Só números NAO_OFICIAL: quanto custaria a mesma mensagem na API Oficial.

custoEvitado.marketingCentavosintegersim
custoEvitado.utilityCentavosintegersim
contatoOptoutbooleansim

O contato pediu para sair; o envio não é bloqueado, só sinalizado.

janelaAbertabooleansim

Janela de 24h aberta (informativo).

erroobjectnão

Só com status: FALHOU: o erro do canal (na Meta, com o código dela).

erro.codigostringsim
erro.codigoExternostring | nullsim
erro.mensagemstringsim
custoEstimadoobject | nullnão

Só em templates: estimativa pela tabela pública.

custoEstimado.categoriaenumsim

Valores: MARKETING, UTILITY, AUTHENTICATION.

custoEstimado.centavosintegersim

Estimativa do que a Meta cobra por esta mensagem, em centavos de BRL.

custoEstimado.precoUsdstringsim
custoEstimado.cambiostringsim
custoEstimado.tabelaVersaointegersim
custoEstimado.estimadoenumsim

Valores: true.

Exemplo de resposta
{
  "id": "clx0mensagem0000000000001",
  "tipoNumero": "NAO_OFICIAL",
  "tipoEnvio": "PRIMEIRO_CONTATO",
  "status": "ENVIADA",
  "enfileirada": false,
  "custoEvitado": {
    "marketingCentavos": 34,
    "utilityCentavos": 7
  },
  "contatoOptout": false,
  "janelaAberta": false
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/mensagens/enquete

Envia uma enquete (número não oficial)

Responde 202: a mensagem foi aceita e o resultado final chega no evento mensagem.status. Em número OFICIAL tudo é encaminhado à Meta sem bloqueio do ZapHub; se a Meta recusar (por exemplo, texto livre fora da janela de 24h), a resposta traz status: FALHOU e erro com o código e a mensagem da Meta. Em NAO_OFICIAL não há bloqueio por volume: acima do ritmo do número a mensagem fica enfileirada e sai logo em seguida.

Somente números NAO_OFICIAL (no oficial responde 422 recurso_indisponivel). Os votos chegam em mensagem.voto e em GET /v1/mensagens/{id}/votos. Aceita respondendoA (cita uma mensagem da mesma conversa: 404 se ela não é da conta, 422 se é de outra conversa) e mencoes (só não oficial; efeito em grupos). Para grupo, envie grupoId no lugar de para (com mencionarTodos, se quiser): sem opt-out individual, tipo de envio GRUPO, fora da economia e na cadência do número.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Número NAO_OFICIAL. Tamanho: 1 a 64.

parastringnão

Telefone do contato com DDI e DDD. Aceita máscara (+55 (11) 98765-4321); sem DDI e com 10 ou 11 dígitos assume o Brasil (55). Normalizado para E.164 sem +. Tamanho: 8 a 30.

perguntastringsim

Tamanho: 1 a 255.

opcoesstring[]sim

No máximo 12 itens.

maxRespostasintegernão

Quantas opções cada contato pode marcar. Intervalo: 1 a ∞. Padrão: 1.

respondendoAstringnão

id de uma mensagem da mesma conversa: o envio sai citando ela. Tamanho: 1 a 64.

mencoesstring[]não

Telefones mencionados (só em grupos; em conversa individual é aceito e ignorado). Só NAO_OFICIAL. No máximo 50 itens.

grupoIdstringnão

Envia ao grupo (id de GET /v1/numeros/{id}/grupos) em vez de para. Só NAO_OFICIAL. Tamanho: 1 a 64.

mencionarTodosbooleannão

Só em grupo: menciona todos os participantes.

Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001",
  "para": "5511987654321",
  "pergunta": "Qual o melhor dia para a entrega?",
  "opcoes": [
    "Sexta-feira",
    "Sábado"
  ],
  "maxRespostas": 1
}

Resposta 202 · Aceito

Mensagem aceita.

CampoTipoObrig.Descrição
idstringsim

Identificador da mensagem (GET /v1/mensagens/{id}). O resultado final chega em mensagem.status.

tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

tipoEnvioenumsim

Valores: PRIMEIRO_CONTATO, PRIMEIRA_RESPOSTA, CONVERSA, RETOMADA, CONTATO_LEAD, AUTO_RESPOSTA, CAMPANHA, GRUPO.

statusenumsim

Valores: ENFILEIRADA, ENVIADA, ENTREGUE, LIDA, FALHOU, RECEBIDA.

enfileiradabooleansim

true = na fila de cadência do número (não oficial); sai logo em seguida.

custoEvitadoobject | nullsim

Só números NAO_OFICIAL: quanto custaria a mesma mensagem na API Oficial.

custoEvitado.marketingCentavosintegersim
custoEvitado.utilityCentavosintegersim
contatoOptoutbooleansim

O contato pediu para sair; o envio não é bloqueado, só sinalizado.

janelaAbertabooleansim

Janela de 24h aberta (informativo).

erroobjectnão

Só com status: FALHOU: o erro do canal (na Meta, com o código dela).

erro.codigostringsim
erro.codigoExternostring | nullsim
erro.mensagemstringsim
custoEstimadoobject | nullnão

Só em templates: estimativa pela tabela pública.

custoEstimado.categoriaenumsim

Valores: MARKETING, UTILITY, AUTHENTICATION.

custoEstimado.centavosintegersim

Estimativa do que a Meta cobra por esta mensagem, em centavos de BRL.

custoEstimado.precoUsdstringsim
custoEstimado.cambiostringsim
custoEstimado.tabelaVersaointegersim
custoEstimado.estimadoenumsim

Valores: true.

Exemplo de resposta
{
  "id": "clx0mensagem0000000000001",
  "tipoNumero": "NAO_OFICIAL",
  "tipoEnvio": "PRIMEIRO_CONTATO",
  "status": "ENVIADA",
  "enfileirada": false,
  "custoEvitado": {
    "marketingCentavos": 34,
    "utilityCentavos": 7
  },
  "contatoOptout": false,
  "janelaAberta": false
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/mensagens/template

Envia um template (número oficial)

Responde 202: a mensagem foi aceita e o resultado final chega no evento mensagem.status. Em número OFICIAL tudo é encaminhado à Meta sem bloqueio do ZapHub; se a Meta recusar (por exemplo, texto livre fora da janela de 24h), a resposta traz status: FALHOU e erro com o código e a mensagem da Meta. Em NAO_OFICIAL não há bloqueio por volume: acima do ritmo do número a mensagem fica enfileirada e sai logo em seguida.

Somente números OFICIAL. A resposta inclui custoEstimado (pela tabela pública de preços da Meta e o câmbio vigente), quando a tabela está configurada.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Número OFICIAL. Tamanho: 1 a 64.

parastringsim

Telefone do contato com DDI e DDD. Aceita máscara (+55 (11) 98765-4321); sem DDI e com 10 ou 11 dígitos assume o Brasil (55). Normalizado para E.164 sem +. Tamanho: 8 a 30.

templateIdstringsim

id do template em GET /v1/numeros/{id}/templates. Tamanho: 1 a 64.

variaveisstring[]não

Valores das variáveis {{1}}, {{2}}… na ordem (cabeçalho, corpo, botões). No máximo 30 itens. Padrão: [].

Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001",
  "para": "5511987654321",
  "templateId": "clx0template0000000000001",
  "variaveis": [
    "Ana",
    "COD-1042"
  ]
}

Resposta 202 · Aceito

Mensagem aceita.

CampoTipoObrig.Descrição
idstringsim

Identificador da mensagem (GET /v1/mensagens/{id}). O resultado final chega em mensagem.status.

tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

tipoEnvioenumsim

Valores: PRIMEIRO_CONTATO, PRIMEIRA_RESPOSTA, CONVERSA, RETOMADA, CONTATO_LEAD, AUTO_RESPOSTA, CAMPANHA, GRUPO.

statusenumsim

Valores: ENFILEIRADA, ENVIADA, ENTREGUE, LIDA, FALHOU, RECEBIDA.

enfileiradabooleansim

true = na fila de cadência do número (não oficial); sai logo em seguida.

custoEvitadoobject | nullsim

Só números NAO_OFICIAL: quanto custaria a mesma mensagem na API Oficial.

custoEvitado.marketingCentavosintegersim
custoEvitado.utilityCentavosintegersim
contatoOptoutbooleansim

O contato pediu para sair; o envio não é bloqueado, só sinalizado.

janelaAbertabooleansim

Janela de 24h aberta (informativo).

erroobjectnão

Só com status: FALHOU: o erro do canal (na Meta, com o código dela).

erro.codigostringsim
erro.codigoExternostring | nullsim
erro.mensagemstringsim
custoEstimadoobject | nullnão

Só em templates: estimativa pela tabela pública.

custoEstimado.categoriaenumsim

Valores: MARKETING, UTILITY, AUTHENTICATION.

custoEstimado.centavosintegersim

Estimativa do que a Meta cobra por esta mensagem, em centavos de BRL.

custoEstimado.precoUsdstringsim
custoEstimado.cambiostringsim
custoEstimado.tabelaVersaointegersim
custoEstimado.estimadoenumsim

Valores: true.

Exemplo de resposta
{
  "id": "clx0mensagem0000000000001",
  "tipoNumero": "OFICIAL",
  "tipoEnvio": "PRIMEIRO_CONTATO",
  "status": "ENVIADA",
  "enfileirada": false,
  "custoEvitado": null,
  "contatoOptout": false,
  "janelaAberta": false,
  "custoEstimado": {
    "categoria": "MARKETING",
    "centavos": 34,
    "precoUsd": "0.0625",
    "cambio": "5.4",
    "tabelaVersao": 1,
    "estimado": true
  }
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
get /v1/mensagens/{id}

Consulta uma mensagem

Status, tipo do número, tipo de envio, erro do canal (quando falhou) e, em número não oficial, o custo evitado. Depois da retenção de 90 dias o texto some (removida: true).

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da mensagem (id devolvido no envio ou no evento).

Resposta 200 · OK

A mensagem.

CampoTipoObrig.Descrição
idstringsim
numeroIdstringsim
tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

conversaIdstringsim
contatoobjectsim
contato.telefonestringsim
direcaoenumsim

Valores: ENTRADA, SAIDA.

origemenumsim

Valores: CONTATO, CELULAR, PAINEL, API, AUTO, CAMPANHA, REPASSE.

tipoEnvioenum | nullsim

Valores: PRIMEIRO_CONTATO, PRIMEIRA_RESPOSTA, CONVERSA, RETOMADA, CONTATO_LEAD, AUTO_RESPOSTA, CAMPANHA, GRUPO.

tipoConteudoenumsim

Valores: TEXTO, IMAGEM, AUDIO, VIDEO, DOCUMENTO, LOCALIZACAO, TEMPLATE, CONTATO, FIGURINHA, ENQUETE, OUTRO.

textostring | nullsim

Nulo depois da retenção (removida).

midiaobject | nullsim
midia.tipoenumsim

Valores: IMAGEM, AUDIO, VIDEO, DOCUMENTO.

midia.mimeTypestring | nullsim
midia.disponivelbooleansim
statusenumsim

Valores: ENFILEIRADA, ENVIADA, ENTREGUE, LIDA, FALHOU, RECEBIDA.

enfileiradabooleansim
contatoOptoutbooleansim
custoEvitadoobject | nullsim
custoEvitado.marketingCentavosintegersim
custoEvitado.utilityCentavosintegersim
erroobject | nullsim
erro.codigostring | nullsim
erro.mensagemstring | nullsim
criadoEmstringsim
enviadoEmstring | nullsim
entregueEmstring | nullsim
lidoEmstring | nullsim
removidabooleansim

Conteúdo removido pela retenção de 90 dias; os metadados permanecem.

editadaEmstring | nullsim

O autor editou a mensagem (o texto já é o novo).

apagadaEmstring | nullsim

O autor apagou para todos: texto e mídia foram limpos.

reacoesobject[]sim

Reação atual de cada lado.

reacoes[].autorenumsim

Valores: contato, numero.

reacoes[].emojistringsim
contatosobject[] | nullsim

Só em tipoConteudo: CONTATO.

contatos[].nomestringsim
contatos[].telefonesstring[]sim
contatos[].empresastringnão
contatos[].emailstringnão
enqueteobject | nullsim

Só em enquetes enviadas pelo número; o detalhe por contato está em GET /v1/mensagens/{id}/votos.

enquete.maxRespostasintegersim
enquete.opcoesobject[]sim
enquete.opcoes[].opcaostringsim
enquete.opcoes[].totalintegersim
respondendoAobject | nullsim

Mensagem citada.

respondendoA.idstringsim
respondendoA.trechostring | nullsim
remetenteobject | nullsim

Só em grupo: quem escreveu.

remetente.telefonestring | nullsim
remetente.nomestring | nullsim
interativoobject | nullsim

Mensagem interativa enviada (oficial): o tipo e os títulos das opções.

interativo.tipoenumsim

Valores: botoes, lista, link.

interativo.opcoesstring[]sim
interacaoobject | nullsim

Resposta do contato a uma interativa: o botão ou item escolhido.

interacao.tipoenumsim

Valores: botao, lista.

interacao.idstringsim
interacao.titulostringsim
Exemplo de resposta
{
  "id": "texto",
  "numeroId": "texto",
  "tipoNumero": "OFICIAL",
  "conversaId": "texto",
  "contato": {
    "telefone": "texto"
  },
  "direcao": "ENTRADA",
  "origem": "CONTATO",
  "tipoEnvio": "PRIMEIRO_CONTATO",
  "tipoConteudo": "TEXTO",
  "texto": "texto",
  "midia": {
    "tipo": "IMAGEM",
    "mimeType": "texto",
    "disponivel": false
  },
  "status": "ENFILEIRADA",
  "enfileirada": false,
  "contatoOptout": false,
  "custoEvitado": {
    "marketingCentavos": 0,
    "utilityCentavos": 0
  },
  "erro": {
    "codigo": "texto",
    "mensagem": "texto"
  },
  "criadoEm": "texto",
  "enviadoEm": "texto",
  "entregueEm": "texto",
  "lidoEm": "texto",
  "removida": false,
  "editadaEm": "texto",
  "apagadaEm": "texto",
  "reacoes": [
    {
      "autor": "contato",
      "emoji": "texto"
    }
  ],
  "contatos": [
    {
      "nome": "texto",
      "telefones": [
        "texto"
      ],
      "empresa": "texto",
      "email": "texto"
    }
  ],
  "enquete": {
    "maxRespostas": 0,
    "opcoes": [
      {
        "opcao": "texto",
        "total": 0
      }
    ]
  },
  "respondendoA": {
    "id": "texto",
    "trecho": "texto"
  },
  "remetente": {
    "telefone": "texto",
    "nome": "texto"
  },
  "interativo": {
    "tipo": "botoes",
    "opcoes": [
      "texto"
    ]
  },
  "interacao": {
    "tipo": "botao",
    "id": "texto",
    "titulo": "texto"
  }
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
patch /v1/mensagens/{id}

Edita uma mensagem enviada (número não oficial)

Só mensagens de texto enviadas pelo número, até 15 minutos depois do envio (regra do WhatsApp; fora do prazo, 422). No oficial responde 422 recurso_indisponivel. Emite mensagem.editada. Não é mensagem nova: não passa pela cadência nem entra na economia. O canal é chamado primeiro; se ele recusar, responde 502 com o código do canal e nada muda. Com o aceite, a mensagem guardada muda e o evento correspondente sai com autor: "numero".

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da mensagem.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
textostringsim

Tamanho: 1 a 4096.

Exemplo de corpo
{
  "texto": "Corrigindo: entrega no sábado."
}

Resposta 200 · OK

A mensagem editada.

CampoTipoObrig.Descrição
idstringsim
numeroIdstringsim
tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

conversaIdstringsim
contatoobjectsim
contato.telefonestringsim
direcaoenumsim

Valores: ENTRADA, SAIDA.

origemenumsim

Valores: CONTATO, CELULAR, PAINEL, API, AUTO, CAMPANHA, REPASSE.

tipoEnvioenum | nullsim

Valores: PRIMEIRO_CONTATO, PRIMEIRA_RESPOSTA, CONVERSA, RETOMADA, CONTATO_LEAD, AUTO_RESPOSTA, CAMPANHA, GRUPO.

tipoConteudoenumsim

Valores: TEXTO, IMAGEM, AUDIO, VIDEO, DOCUMENTO, LOCALIZACAO, TEMPLATE, CONTATO, FIGURINHA, ENQUETE, OUTRO.

textostring | nullsim

Nulo depois da retenção (removida).

midiaobject | nullsim
midia.tipoenumsim

Valores: IMAGEM, AUDIO, VIDEO, DOCUMENTO.

midia.mimeTypestring | nullsim
midia.disponivelbooleansim
statusenumsim

Valores: ENFILEIRADA, ENVIADA, ENTREGUE, LIDA, FALHOU, RECEBIDA.

enfileiradabooleansim
contatoOptoutbooleansim
custoEvitadoobject | nullsim
custoEvitado.marketingCentavosintegersim
custoEvitado.utilityCentavosintegersim
erroobject | nullsim
erro.codigostring | nullsim
erro.mensagemstring | nullsim
criadoEmstringsim
enviadoEmstring | nullsim
entregueEmstring | nullsim
lidoEmstring | nullsim
removidabooleansim

Conteúdo removido pela retenção de 90 dias; os metadados permanecem.

editadaEmstring | nullsim

O autor editou a mensagem (o texto já é o novo).

apagadaEmstring | nullsim

O autor apagou para todos: texto e mídia foram limpos.

reacoesobject[]sim

Reação atual de cada lado.

reacoes[].autorenumsim

Valores: contato, numero.

reacoes[].emojistringsim
contatosobject[] | nullsim

Só em tipoConteudo: CONTATO.

contatos[].nomestringsim
contatos[].telefonesstring[]sim
contatos[].empresastringnão
contatos[].emailstringnão
enqueteobject | nullsim

Só em enquetes enviadas pelo número; o detalhe por contato está em GET /v1/mensagens/{id}/votos.

enquete.maxRespostasintegersim
enquete.opcoesobject[]sim
enquete.opcoes[].opcaostringsim
enquete.opcoes[].totalintegersim
respondendoAobject | nullsim

Mensagem citada.

respondendoA.idstringsim
respondendoA.trechostring | nullsim
remetenteobject | nullsim

Só em grupo: quem escreveu.

remetente.telefonestring | nullsim
remetente.nomestring | nullsim
interativoobject | nullsim

Mensagem interativa enviada (oficial): o tipo e os títulos das opções.

interativo.tipoenumsim

Valores: botoes, lista, link.

interativo.opcoesstring[]sim
interacaoobject | nullsim

Resposta do contato a uma interativa: o botão ou item escolhido.

interacao.tipoenumsim

Valores: botao, lista.

interacao.idstringsim
interacao.titulostringsim
Exemplo de resposta
{
  "id": "texto",
  "numeroId": "texto",
  "tipoNumero": "OFICIAL",
  "conversaId": "texto",
  "contato": {
    "telefone": "texto"
  },
  "direcao": "ENTRADA",
  "origem": "CONTATO",
  "tipoEnvio": "PRIMEIRO_CONTATO",
  "tipoConteudo": "TEXTO",
  "texto": "texto",
  "midia": {
    "tipo": "IMAGEM",
    "mimeType": "texto",
    "disponivel": false
  },
  "status": "ENFILEIRADA",
  "enfileirada": false,
  "contatoOptout": false,
  "custoEvitado": {
    "marketingCentavos": 0,
    "utilityCentavos": 0
  },
  "erro": {
    "codigo": "texto",
    "mensagem": "texto"
  },
  "criadoEm": "texto",
  "enviadoEm": "texto",
  "entregueEm": "texto",
  "lidoEm": "texto",
  "removida": false,
  "editadaEm": "texto",
  "apagadaEm": "texto",
  "reacoes": [
    {
      "autor": "contato",
      "emoji": "texto"
    }
  ],
  "contatos": [
    {
      "nome": "texto",
      "telefones": [
        "texto"
      ],
      "empresa": "texto",
      "email": "texto"
    }
  ],
  "enquete": {
    "maxRespostas": 0,
    "opcoes": [
      {
        "opcao": "texto",
        "total": 0
      }
    ]
  },
  "respondendoA": {
    "id": "texto",
    "trecho": "texto"
  },
  "remetente": {
    "telefone": "texto",
    "nome": "texto"
  },
  "interativo": {
    "tipo": "botoes",
    "opcoes": [
      "texto"
    ]
  },
  "interacao": {
    "tipo": "botao",
    "id": "texto",
    "titulo": "texto"
  }
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
delete /v1/mensagens/{id}

Apaga para todos (número não oficial)

Só mensagens enviadas pelo número, até 2 dias depois do envio. Texto e mídia são limpos. No oficial responde 422 recurso_indisponivel. Emite mensagem.apagada. Não é mensagem nova: não passa pela cadência nem entra na economia. O canal é chamado primeiro; se ele recusar, responde 502 com o código do canal e nada muda. Com o aceite, a mensagem guardada muda e o evento correspondente sai com autor: "numero".

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da mensagem.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Resposta 200 · OK

A mensagem apagada.

CampoTipoObrig.Descrição
idstringsim
numeroIdstringsim
tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

conversaIdstringsim
contatoobjectsim
contato.telefonestringsim
direcaoenumsim

Valores: ENTRADA, SAIDA.

origemenumsim

Valores: CONTATO, CELULAR, PAINEL, API, AUTO, CAMPANHA, REPASSE.

tipoEnvioenum | nullsim

Valores: PRIMEIRO_CONTATO, PRIMEIRA_RESPOSTA, CONVERSA, RETOMADA, CONTATO_LEAD, AUTO_RESPOSTA, CAMPANHA, GRUPO.

tipoConteudoenumsim

Valores: TEXTO, IMAGEM, AUDIO, VIDEO, DOCUMENTO, LOCALIZACAO, TEMPLATE, CONTATO, FIGURINHA, ENQUETE, OUTRO.

textostring | nullsim

Nulo depois da retenção (removida).

midiaobject | nullsim
midia.tipoenumsim

Valores: IMAGEM, AUDIO, VIDEO, DOCUMENTO.

midia.mimeTypestring | nullsim
midia.disponivelbooleansim
statusenumsim

Valores: ENFILEIRADA, ENVIADA, ENTREGUE, LIDA, FALHOU, RECEBIDA.

enfileiradabooleansim
contatoOptoutbooleansim
custoEvitadoobject | nullsim
custoEvitado.marketingCentavosintegersim
custoEvitado.utilityCentavosintegersim
erroobject | nullsim
erro.codigostring | nullsim
erro.mensagemstring | nullsim
criadoEmstringsim
enviadoEmstring | nullsim
entregueEmstring | nullsim
lidoEmstring | nullsim
removidabooleansim

Conteúdo removido pela retenção de 90 dias; os metadados permanecem.

editadaEmstring | nullsim

O autor editou a mensagem (o texto já é o novo).

apagadaEmstring | nullsim

O autor apagou para todos: texto e mídia foram limpos.

reacoesobject[]sim

Reação atual de cada lado.

reacoes[].autorenumsim

Valores: contato, numero.

reacoes[].emojistringsim
contatosobject[] | nullsim

Só em tipoConteudo: CONTATO.

contatos[].nomestringsim
contatos[].telefonesstring[]sim
contatos[].empresastringnão
contatos[].emailstringnão
enqueteobject | nullsim

Só em enquetes enviadas pelo número; o detalhe por contato está em GET /v1/mensagens/{id}/votos.

enquete.maxRespostasintegersim
enquete.opcoesobject[]sim
enquete.opcoes[].opcaostringsim
enquete.opcoes[].totalintegersim
respondendoAobject | nullsim

Mensagem citada.

respondendoA.idstringsim
respondendoA.trechostring | nullsim
remetenteobject | nullsim

Só em grupo: quem escreveu.

remetente.telefonestring | nullsim
remetente.nomestring | nullsim
interativoobject | nullsim

Mensagem interativa enviada (oficial): o tipo e os títulos das opções.

interativo.tipoenumsim

Valores: botoes, lista, link.

interativo.opcoesstring[]sim
interacaoobject | nullsim

Resposta do contato a uma interativa: o botão ou item escolhido.

interacao.tipoenumsim

Valores: botao, lista.

interacao.idstringsim
interacao.titulostringsim
Exemplo de resposta
{
  "id": "texto",
  "numeroId": "texto",
  "tipoNumero": "OFICIAL",
  "conversaId": "texto",
  "contato": {
    "telefone": "texto"
  },
  "direcao": "ENTRADA",
  "origem": "CONTATO",
  "tipoEnvio": "PRIMEIRO_CONTATO",
  "tipoConteudo": "TEXTO",
  "texto": "texto",
  "midia": {
    "tipo": "IMAGEM",
    "mimeType": "texto",
    "disponivel": false
  },
  "status": "ENFILEIRADA",
  "enfileirada": false,
  "contatoOptout": false,
  "custoEvitado": {
    "marketingCentavos": 0,
    "utilityCentavos": 0
  },
  "erro": {
    "codigo": "texto",
    "mensagem": "texto"
  },
  "criadoEm": "texto",
  "enviadoEm": "texto",
  "entregueEm": "texto",
  "lidoEm": "texto",
  "removida": false,
  "editadaEm": "texto",
  "apagadaEm": "texto",
  "reacoes": [
    {
      "autor": "contato",
      "emoji": "texto"
    }
  ],
  "contatos": [
    {
      "nome": "texto",
      "telefones": [
        "texto"
      ],
      "empresa": "texto",
      "email": "texto"
    }
  ],
  "enquete": {
    "maxRespostas": 0,
    "opcoes": [
      {
        "opcao": "texto",
        "total": 0
      }
    ]
  },
  "respondendoA": {
    "id": "texto",
    "trecho": "texto"
  },
  "remetente": {
    "telefone": "texto",
    "nome": "texto"
  },
  "interativo": {
    "tipo": "botoes",
    "opcoes": [
      "texto"
    ]
  },
  "interacao": {
    "tipo": "botao",
    "id": "texto",
    "titulo": "texto"
  }
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/mensagens/{id}/reacao

Reage a uma mensagem

Reação do número a qualquer mensagem da conversa (emoji vazio remove). Emite mensagem.reacao. Não é mensagem nova: não passa pela cadência nem entra na economia. O canal é chamado primeiro; se ele recusar, responde 502 com o código do canal e nada muda. Com o aceite, a mensagem guardada muda e o evento correspondente sai com autor: "numero".

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da mensagem.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
emojistringsim

Vazio remove a reação do número. Tamanho: 0 a 16.

Exemplo de corpo
{
  "emoji": "✅"
}

Resposta 200 · OK

A mensagem, com a reação.

CampoTipoObrig.Descrição
idstringsim
numeroIdstringsim
tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

conversaIdstringsim
contatoobjectsim
contato.telefonestringsim
direcaoenumsim

Valores: ENTRADA, SAIDA.

origemenumsim

Valores: CONTATO, CELULAR, PAINEL, API, AUTO, CAMPANHA, REPASSE.

tipoEnvioenum | nullsim

Valores: PRIMEIRO_CONTATO, PRIMEIRA_RESPOSTA, CONVERSA, RETOMADA, CONTATO_LEAD, AUTO_RESPOSTA, CAMPANHA, GRUPO.

tipoConteudoenumsim

Valores: TEXTO, IMAGEM, AUDIO, VIDEO, DOCUMENTO, LOCALIZACAO, TEMPLATE, CONTATO, FIGURINHA, ENQUETE, OUTRO.

textostring | nullsim

Nulo depois da retenção (removida).

midiaobject | nullsim
midia.tipoenumsim

Valores: IMAGEM, AUDIO, VIDEO, DOCUMENTO.

midia.mimeTypestring | nullsim
midia.disponivelbooleansim
statusenumsim

Valores: ENFILEIRADA, ENVIADA, ENTREGUE, LIDA, FALHOU, RECEBIDA.

enfileiradabooleansim
contatoOptoutbooleansim
custoEvitadoobject | nullsim
custoEvitado.marketingCentavosintegersim
custoEvitado.utilityCentavosintegersim
erroobject | nullsim
erro.codigostring | nullsim
erro.mensagemstring | nullsim
criadoEmstringsim
enviadoEmstring | nullsim
entregueEmstring | nullsim
lidoEmstring | nullsim
removidabooleansim

Conteúdo removido pela retenção de 90 dias; os metadados permanecem.

editadaEmstring | nullsim

O autor editou a mensagem (o texto já é o novo).

apagadaEmstring | nullsim

O autor apagou para todos: texto e mídia foram limpos.

reacoesobject[]sim

Reação atual de cada lado.

reacoes[].autorenumsim

Valores: contato, numero.

reacoes[].emojistringsim
contatosobject[] | nullsim

Só em tipoConteudo: CONTATO.

contatos[].nomestringsim
contatos[].telefonesstring[]sim
contatos[].empresastringnão
contatos[].emailstringnão
enqueteobject | nullsim

Só em enquetes enviadas pelo número; o detalhe por contato está em GET /v1/mensagens/{id}/votos.

enquete.maxRespostasintegersim
enquete.opcoesobject[]sim
enquete.opcoes[].opcaostringsim
enquete.opcoes[].totalintegersim
respondendoAobject | nullsim

Mensagem citada.

respondendoA.idstringsim
respondendoA.trechostring | nullsim
remetenteobject | nullsim

Só em grupo: quem escreveu.

remetente.telefonestring | nullsim
remetente.nomestring | nullsim
interativoobject | nullsim

Mensagem interativa enviada (oficial): o tipo e os títulos das opções.

interativo.tipoenumsim

Valores: botoes, lista, link.

interativo.opcoesstring[]sim
interacaoobject | nullsim

Resposta do contato a uma interativa: o botão ou item escolhido.

interacao.tipoenumsim

Valores: botao, lista.

interacao.idstringsim
interacao.titulostringsim
Exemplo de resposta
{
  "id": "texto",
  "numeroId": "texto",
  "tipoNumero": "OFICIAL",
  "conversaId": "texto",
  "contato": {
    "telefone": "texto"
  },
  "direcao": "ENTRADA",
  "origem": "CONTATO",
  "tipoEnvio": "PRIMEIRO_CONTATO",
  "tipoConteudo": "TEXTO",
  "texto": "texto",
  "midia": {
    "tipo": "IMAGEM",
    "mimeType": "texto",
    "disponivel": false
  },
  "status": "ENFILEIRADA",
  "enfileirada": false,
  "contatoOptout": false,
  "custoEvitado": {
    "marketingCentavos": 0,
    "utilityCentavos": 0
  },
  "erro": {
    "codigo": "texto",
    "mensagem": "texto"
  },
  "criadoEm": "texto",
  "enviadoEm": "texto",
  "entregueEm": "texto",
  "lidoEm": "texto",
  "removida": false,
  "editadaEm": "texto",
  "apagadaEm": "texto",
  "reacoes": [
    {
      "autor": "contato",
      "emoji": "texto"
    }
  ],
  "contatos": [
    {
      "nome": "texto",
      "telefones": [
        "texto"
      ],
      "empresa": "texto",
      "email": "texto"
    }
  ],
  "enquete": {
    "maxRespostas": 0,
    "opcoes": [
      {
        "opcao": "texto",
        "total": 0
      }
    ]
  },
  "respondendoA": {
    "id": "texto",
    "trecho": "texto"
  },
  "remetente": {
    "telefone": "texto",
    "nome": "texto"
  },
  "interativo": {
    "tipo": "botoes",
    "opcoes": [
      "texto"
    ]
  },
  "interacao": {
    "tipo": "botao",
    "id": "texto",
    "titulo": "texto"
  }
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
get /v1/mensagens/{id}/votos

Resultado de uma enquete

Total de cada opção e o voto atual de cada contato. Só enquetes enviadas pelo número (outra mensagem → 422).

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da enquete.

Resposta 200 · OK

Os votos.

CampoTipoObrig.Descrição
mensagemIdstringsim
perguntastring | nullsim
maxRespostasintegersim
opcoesobject[]sim

Na ordem da enquete, com o total de votos atuais.

opcoes[].opcaostringsim
opcoes[].totalintegersim
opcoes[].telefonesstring[]sim
votosobject[]sim

Voto atual de cada contato (trocar o voto substitui o anterior).

votos[].telefonestringsim
votos[].opcoesstring[]sim
votos[].votadoEmstringsim
Exemplo de resposta
{
  "mensagemId": "clx0mensagem00000000000001",
  "pergunta": "Qual o melhor dia para a entrega?",
  "maxRespostas": 1,
  "opcoes": [
    {
      "opcao": "Sexta-feira",
      "total": 1,
      "telefones": [
        "5511987654321"
      ]
    },
    {
      "opcao": "Sábado",
      "total": 0,
      "telefones": []
    }
  ],
  "votos": [
    {
      "telefone": "5511987654321",
      "opcoes": [
        "Sexta-feira"
      ],
      "votadoEm": "2026-10-14T13:07:00.000Z"
    }
  ]
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/mensagens/{id}/lida

Marca uma mensagem recebida como lida

O contato vê os tiques azuis (se a privacidade dele permitir), e a conversa fica com naoLidas = 0. Só mensagens recebidas do contato.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da mensagem recebida.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Resposta 200 · OK

A mensagem.

CampoTipoObrig.Descrição
idstringsim
numeroIdstringsim
tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

conversaIdstringsim
contatoobjectsim
contato.telefonestringsim
direcaoenumsim

Valores: ENTRADA, SAIDA.

origemenumsim

Valores: CONTATO, CELULAR, PAINEL, API, AUTO, CAMPANHA, REPASSE.

tipoEnvioenum | nullsim

Valores: PRIMEIRO_CONTATO, PRIMEIRA_RESPOSTA, CONVERSA, RETOMADA, CONTATO_LEAD, AUTO_RESPOSTA, CAMPANHA, GRUPO.

tipoConteudoenumsim

Valores: TEXTO, IMAGEM, AUDIO, VIDEO, DOCUMENTO, LOCALIZACAO, TEMPLATE, CONTATO, FIGURINHA, ENQUETE, OUTRO.

textostring | nullsim

Nulo depois da retenção (removida).

midiaobject | nullsim
midia.tipoenumsim

Valores: IMAGEM, AUDIO, VIDEO, DOCUMENTO.

midia.mimeTypestring | nullsim
midia.disponivelbooleansim
statusenumsim

Valores: ENFILEIRADA, ENVIADA, ENTREGUE, LIDA, FALHOU, RECEBIDA.

enfileiradabooleansim
contatoOptoutbooleansim
custoEvitadoobject | nullsim
custoEvitado.marketingCentavosintegersim
custoEvitado.utilityCentavosintegersim
erroobject | nullsim
erro.codigostring | nullsim
erro.mensagemstring | nullsim
criadoEmstringsim
enviadoEmstring | nullsim
entregueEmstring | nullsim
lidoEmstring | nullsim
removidabooleansim

Conteúdo removido pela retenção de 90 dias; os metadados permanecem.

editadaEmstring | nullsim

O autor editou a mensagem (o texto já é o novo).

apagadaEmstring | nullsim

O autor apagou para todos: texto e mídia foram limpos.

reacoesobject[]sim

Reação atual de cada lado.

reacoes[].autorenumsim

Valores: contato, numero.

reacoes[].emojistringsim
contatosobject[] | nullsim

Só em tipoConteudo: CONTATO.

contatos[].nomestringsim
contatos[].telefonesstring[]sim
contatos[].empresastringnão
contatos[].emailstringnão
enqueteobject | nullsim

Só em enquetes enviadas pelo número; o detalhe por contato está em GET /v1/mensagens/{id}/votos.

enquete.maxRespostasintegersim
enquete.opcoesobject[]sim
enquete.opcoes[].opcaostringsim
enquete.opcoes[].totalintegersim
respondendoAobject | nullsim

Mensagem citada.

respondendoA.idstringsim
respondendoA.trechostring | nullsim
remetenteobject | nullsim

Só em grupo: quem escreveu.

remetente.telefonestring | nullsim
remetente.nomestring | nullsim
interativoobject | nullsim

Mensagem interativa enviada (oficial): o tipo e os títulos das opções.

interativo.tipoenumsim

Valores: botoes, lista, link.

interativo.opcoesstring[]sim
interacaoobject | nullsim

Resposta do contato a uma interativa: o botão ou item escolhido.

interacao.tipoenumsim

Valores: botao, lista.

interacao.idstringsim
interacao.titulostringsim
Exemplo de resposta
{
  "id": "texto",
  "numeroId": "texto",
  "tipoNumero": "OFICIAL",
  "conversaId": "texto",
  "contato": {
    "telefone": "texto"
  },
  "direcao": "ENTRADA",
  "origem": "CONTATO",
  "tipoEnvio": "PRIMEIRO_CONTATO",
  "tipoConteudo": "TEXTO",
  "texto": "texto",
  "midia": {
    "tipo": "IMAGEM",
    "mimeType": "texto",
    "disponivel": false
  },
  "status": "ENFILEIRADA",
  "enfileirada": false,
  "contatoOptout": false,
  "custoEvitado": {
    "marketingCentavos": 0,
    "utilityCentavos": 0
  },
  "erro": {
    "codigo": "texto",
    "mensagem": "texto"
  },
  "criadoEm": "texto",
  "enviadoEm": "texto",
  "entregueEm": "texto",
  "lidoEm": "texto",
  "removida": false,
  "editadaEm": "texto",
  "apagadaEm": "texto",
  "reacoes": [
    {
      "autor": "contato",
      "emoji": "texto"
    }
  ],
  "contatos": [
    {
      "nome": "texto",
      "telefones": [
        "texto"
      ],
      "empresa": "texto",
      "email": "texto"
    }
  ],
  "enquete": {
    "maxRespostas": 0,
    "opcoes": [
      {
        "opcao": "texto",
        "total": 0
      }
    ]
  },
  "respondendoA": {
    "id": "texto",
    "trecho": "texto"
  },
  "remetente": {
    "telefone": "texto",
    "nome": "texto"
  },
  "interativo": {
    "tipo": "botoes",
    "opcoes": [
      "texto"
    ]
  },
  "interacao": {
    "tipo": "botao",
    "id": "texto",
    "titulo": "texto"
  }
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/conversas/presenca

Mostra "digitando…" ao contato

Não oficial: digitando, gravando ou parou, visível por duracaoMs. Oficial: só digitando, que a Meta mostra por até 25 s ou até a resposta e que exige uma mensagem recebida do contato (sem ela, 422); gravando responde 422 e parou não faz nada.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros). Tamanho: 1 a 64.

parastringsim

Telefone do contato com DDI e DDD. Aceita máscara (+55 (11) 98765-4321); sem DDI e com 10 ou 11 dígitos assume o Brasil (55). Normalizado para E.164 sem +. Tamanho: 8 a 30.

estadoenumsim

Valores: digitando, gravando, parou.

duracaoMsintegernão

Quanto tempo o indicador fica visível (não oficial). No oficial, a Meta mostra por até 25 s ou até a resposta. Intervalo: 1000 a 25000. Padrão: 5000.

Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001",
  "para": "5511987654321",
  "estado": "digitando",
  "duracaoMs": 5000
}

Resposta 204 · Sem conteúdo

Indicador enviado.

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
get /v1/conversas

Histórico de uma conversa

Mensagens de um contato em um número, da mais nova para a mais antiga, paginadas por cursor (proximoCursor → cursor). Respeita a retenção de 90 dias.

Parâmetros de consulta (query string)

ParâmetroTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros).

telefonestringsim

Telefone do contato com DDI e DDD. Aceita máscara (+55 (11) 98765-4321); sem DDI e com 10 ou 11 dígitos assume o Brasil (55). Normalizado para E.164 sem +.

cursorstringnão

proximoCursor da página anterior (mensagens mais antigas).

limiteintegernão

Resposta 200 · OK

A conversa e a página de mensagens.

CampoTipoObrig.Descrição
conversaobjectsim
conversa.idstringsim
conversa.numeroIdstringsim
conversa.tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

conversa.contatoobjectsim
conversa.contato.telefonestringsim
conversa.contato.nomestring | nullsim
conversa.ultimaMensagemEmstringsim
conversa.ultimaEntradaEmstring | nullsim
conversa.janela24hobjectsim
conversa.janela24h.abertabooleansim
conversa.janela24h.expiraEmstring | nullsim
conversa.tipoenumsim

Em GRUPO, contato.telefone é o JID do grupo e contato.nome o nome dele. Valores: CONTATO, GRUPO.

conversa.etiquetasobject[]sim

Etiquetas do WhatsApp Business aplicadas (não oficial).

conversa.etiquetas[].idstringsim
conversa.etiquetas[].nomestringsim
conversa.etiquetas[].corintegersim
conversa.arquivadabooleansim
conversa.fixadabooleansim
conversa.silenciadaAtestring | nullsim

Fim do silêncio; muito no futuro = sempre.

mensagensobjectsim
mensagens.itensobject[]sim

Da mais nova para a mais antiga.

mensagens.itens[].idstringsim
mensagens.itens[].numeroIdstringsim
mensagens.itens[].tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

mensagens.itens[].conversaIdstringsim
mensagens.itens[].contatoobjectsim
mensagens.itens[].contato.telefonestringsim
mensagens.itens[].direcaoenumsim

Valores: ENTRADA, SAIDA.

mensagens.itens[].origemenumsim

Valores: CONTATO, CELULAR, PAINEL, API, AUTO, CAMPANHA, REPASSE.

mensagens.itens[].tipoEnvioenum | nullsim

Valores: PRIMEIRO_CONTATO, PRIMEIRA_RESPOSTA, CONVERSA, RETOMADA, CONTATO_LEAD, AUTO_RESPOSTA, CAMPANHA, GRUPO.

mensagens.itens[].tipoConteudoenumsim

Valores: TEXTO, IMAGEM, AUDIO, VIDEO, DOCUMENTO, LOCALIZACAO, TEMPLATE, CONTATO, FIGURINHA, ENQUETE, OUTRO.

mensagens.itens[].textostring | nullsim

Nulo depois da retenção (removida).

mensagens.itens[].midiaobject | nullsim
mensagens.itens[].midia.tipoenumsim

Valores: IMAGEM, AUDIO, VIDEO, DOCUMENTO.

mensagens.itens[].midia.mimeTypestring | nullsim
mensagens.itens[].midia.disponivelbooleansim
mensagens.itens[].statusenumsim

Valores: ENFILEIRADA, ENVIADA, ENTREGUE, LIDA, FALHOU, RECEBIDA.

mensagens.itens[].enfileiradabooleansim
mensagens.itens[].contatoOptoutbooleansim
mensagens.itens[].custoEvitadoobject | nullsim
mensagens.itens[].custoEvitado.marketingCentavosintegersim
mensagens.itens[].custoEvitado.utilityCentavosintegersim
mensagens.itens[].erroobject | nullsim
mensagens.itens[].erro.codigostring | nullsim
mensagens.itens[].erro.mensagemstring | nullsim
mensagens.itens[].criadoEmstringsim
mensagens.itens[].enviadoEmstring | nullsim
mensagens.itens[].entregueEmstring | nullsim
mensagens.itens[].lidoEmstring | nullsim
mensagens.itens[].removidabooleansim

Conteúdo removido pela retenção de 90 dias; os metadados permanecem.

mensagens.itens[].editadaEmstring | nullsim

O autor editou a mensagem (o texto já é o novo).

mensagens.itens[].apagadaEmstring | nullsim

O autor apagou para todos: texto e mídia foram limpos.

mensagens.itens[].reacoesobject[]sim

Reação atual de cada lado.

mensagens.itens[].reacoes[].autorenumsim

Valores: contato, numero.

mensagens.itens[].reacoes[].emojistringsim
mensagens.itens[].contatosobject[] | nullsim

Só em tipoConteudo: CONTATO.

mensagens.itens[].contatos[].nomestringsim
mensagens.itens[].contatos[].telefonesstring[]sim
mensagens.itens[].contatos[].empresastringnão
mensagens.itens[].contatos[].emailstringnão
mensagens.itens[].enqueteobject | nullsim

Só em enquetes enviadas pelo número; o detalhe por contato está em GET /v1/mensagens/{id}/votos.

mensagens.itens[].enquete.maxRespostasintegersim
mensagens.itens[].enquete.opcoesobject[]sim
mensagens.itens[].enquete.opcoes[].opcaostringsim
mensagens.itens[].enquete.opcoes[].totalintegersim
mensagens.itens[].respondendoAobject | nullsim

Mensagem citada.

mensagens.itens[].respondendoA.idstringsim
mensagens.itens[].respondendoA.trechostring | nullsim
mensagens.itens[].remetenteobject | nullsim

Só em grupo: quem escreveu.

mensagens.itens[].remetente.telefonestring | nullsim
mensagens.itens[].remetente.nomestring | nullsim
mensagens.itens[].interativoobject | nullsim

Mensagem interativa enviada (oficial): o tipo e os títulos das opções.

mensagens.itens[].interativo.tipoenumsim

Valores: botoes, lista, link.

mensagens.itens[].interativo.opcoesstring[]sim
mensagens.itens[].interacaoobject | nullsim

Resposta do contato a uma interativa: o botão ou item escolhido.

mensagens.itens[].interacao.tipoenumsim

Valores: botao, lista.

mensagens.itens[].interacao.idstringsim
mensagens.itens[].interacao.titulostringsim
mensagens.proximoCursorstring | nullsim
Exemplo de resposta
{
  "conversa": {
    "id": "texto",
    "numeroId": "texto",
    "tipoNumero": "OFICIAL",
    "contato": {
      "telefone": "texto",
      "nome": "texto"
    },
    "ultimaMensagemEm": "texto",
    "ultimaEntradaEm": "texto",
    "janela24h": {
      "aberta": false,
      "expiraEm": "texto"
    },
    "tipo": "CONTATO",
    "etiquetas": [
      {
        "id": "texto",
        "nome": "texto",
        "cor": 0
      }
    ],
    "arquivada": false,
    "fixada": false,
    "silenciadaAte": "texto"
  },
  "mensagens": {
    "itens": [
      {
        "id": "texto",
        "numeroId": "texto",
        "tipoNumero": "OFICIAL",
        "conversaId": "texto",
        "contato": {
          "telefone": "texto"
        },
        "direcao": "ENTRADA",
        "origem": "CONTATO",
        "tipoEnvio": "PRIMEIRO_CONTATO",
        "tipoConteudo": "TEXTO",
        "texto": "texto",
        "midia": {
          "tipo": "IMAGEM",
          "mimeType": "texto",
          "disponivel": false
        },
        "status": "ENFILEIRADA",
        "enfileirada": false,
        "contatoOptout": false,
        "custoEvitado": {
          "marketingCentavos": 0,
          "utilityCentavos": 0
        },
        "erro": {
          "codigo": "texto",
          "mensagem": "texto"
        },
        "criadoEm": "texto",
        "enviadoEm": "texto",
        "entregueEm": "texto",
        "lidoEm": "texto",
        "removida": false,
        "editadaEm": "texto",
        "apagadaEm": "texto",
        "reacoes": [
          {
            "autor": null,
            "emoji": null
          }
        ],
        "contatos": [
          {
            "nome": null,
            "telefones": null,
            "empresa": null,
            "email": null
          }
        ],
        "enquete": {
          "maxRespostas": 0,
          "opcoes": [
            null
          ]
        },
        "respondendoA": {
          "id": "texto",
          "trecho": "texto"
        },
        "remetente": {
          "telefone": "texto",
          "nome": "texto"
        },
        "interativo": {
          "tipo": "botoes",
          "opcoes": [
            null
          ]
        },
        "interacao": {
          "tipo": "botao",
          "id": "texto",
          "titulo": "texto"
        }
      }
    ],
    "proximoCursor": "texto"
  }
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.

Contatos e configurações

Números não oficiais: verificar se telefones têm WhatsApp, foto e recado do contato, bloqueio, agenda, perfil, privacidade, configurações do número, organizar conversas, histórico e rejeitar chamadas. No oficial, só o recado do perfil comercial.

post /v1/contatos/verificar

Verifica se telefones têm WhatsApp

Até 100 por chamada. Cada número verifica no máximo 1.000 telefones por hora (configurável pela plataforma); acima disso, 429 com Retry-After e nada vai ao WhatsApp, porque verificação em massa é sinal de automação. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Número NAO_OFICIAL conectado. Tamanho: 1 a 64.

telefonesstring[]sim

No máximo 100 itens.

Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001",
  "telefones": [
    "5511987654321",
    "27998765432"
  ]
}

Resposta 200 · OK

Resultado por telefone.

CampoTipoObrig.Descrição
resultadosobject[]sim
resultados[].telefonestringsim

Como foi enviado.

resultados[].temWhatsappbooleansim
resultados[].telefoneNormalizadostring | nullsim

Como o WhatsApp conhece o número (ex.: com ou sem o nono dígito).

resultados[].contaComercialbooleansim
restanteNaHoraintegersim

Quantos telefones este número ainda pode verificar na hora corrente.

Exemplo de resposta
{
  "resultados": [
    {
      "telefone": "texto",
      "temWhatsapp": false,
      "telefoneNormalizado": "texto",
      "contaComercial": false
    }
  ],
  "restanteNaHora": 0
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
get /v1/contatos/{telefone}

Recado, foto e conta comercial do contato

O que a privacidade do contato deixa ver; campo escondido vem nulo. fotoUrl é um link temporário do WhatsApp (fotoExpiraEm). Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
telefonestringsim

Telefone do contato (E.164, com ou sem máscara).

Parâmetros de consulta (query string)

ParâmetroTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros).

Resposta 200 · OK

O contato.

CampoTipoObrig.Descrição
telefonestringsim
temWhatsappbooleansim
nomestring | nullsim

Nome comercial verificado, quando houver.

recadostring | nullsim
fotoUrlstring | nullsim

Link temporário do WhatsApp; nulo se a privacidade do contato esconde a foto.

fotoExpiraEmstring | nullsim
contaComercialbooleansim
Exemplo de resposta
{
  "telefone": "texto",
  "temWhatsapp": false,
  "nome": "texto",
  "recado": "texto",
  "fotoUrl": "texto",
  "fotoExpiraEm": "texto",
  "contaComercial": false
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
get /v1/contatos/{telefone}/foto

Baixa a foto do contato

Devolve a imagem (JPEG/PNG/WebP) com os mesmos cabeçalhos de segurança da mídia. 404 quando a privacidade esconde a foto. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
telefonestringsim

Telefone do contato (E.164, com ou sem máscara).

Parâmetros de consulta (query string)

ParâmetroTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros).

Resposta 200 · OK

A imagem.

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/contatos/{telefone}/bloquear

Bloqueia o contato

Também registra opt-out do contato na conta (bloqueado não recebe mensagens). Auditado. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
telefonestringsim

Telefone do contato (E.164, com ou sem máscara).

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros). Tamanho: 1 a 64.

Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001"
}

Resposta 204 · Sem conteúdo

Bloqueado.

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/contatos/{telefone}/desbloquear

Desbloqueia o contato

Não desfaz o opt-out (ele pode ter outra origem). Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
telefonestringsim

Telefone do contato (E.164, com ou sem máscara).

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros). Tamanho: 1 a 64.

Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001"
}

Resposta 204 · Sem conteúdo

Desbloqueado.

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
get /v1/numeros/{id}/bloqueados

Contatos bloqueados pelo número

Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do número.

Resposta 200 · OK

Telefones bloqueados.

CampoTipoObrig.Descrição
telefonesstring[]sim
Exemplo de resposta
{
  "telefones": [
    "texto"
  ]
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
get /v1/numeros/{id}/contatos

Agenda do número

Contatos que o WhatsApp do número conhece, lidos do celular sob demanda (cache de 5 minutos); o ZapHub não guarda a agenda. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do número.

Parâmetros de consulta (query string)

ParâmetroTipoObrig.Descrição
cursorinteger | nullnão

Posição (o proximoCursor da página anterior).

limiteintegernão

Resposta 200 · OK

Uma página da agenda.

CampoTipoObrig.Descrição
itensobject[]sim
itens[].telefonestringsim
itens[].nomestring | nullsim

Nome salvo na agenda do celular.

itens[].nomePublicostring | nullsim
itens[].nomeComercialstring | nullsim
totalintegersim
proximoCursorinteger | nullsim
Exemplo de resposta
{
  "itens": [
    {
      "telefone": "texto",
      "nome": "texto",
      "nomePublico": "texto",
      "nomeComercial": "texto"
    }
  ],
  "total": 0,
  "proximoCursor": 0
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
patch /v1/numeros/{id}/perfil

Nome, recado e foto do número

Não oficial: nome, recado e foto (URL HTTPS pública ou base64 JPEG/PNG até 5 MB). Oficial: só o recado, que vai ao perfil comercial da Meta; nome responde 422 (é o nome verificado na Meta) e a foto é trocada no Gerenciador do WhatsApp. Auditado.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do número.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
nomestringnão

Tamanho: 1 a 25.

recadostringnão

Tamanho: 0 a 139.

fotostringnão

JPEG ou PNG em base64 (ou data URL), até 5 MB. Tamanho: 0 a 7000000.

Exemplo de corpo
{
  "nome": "Loja Centro",
  "recado": "Atendemos das 8h às 18h"
}

Resposta 204 · Sem conteúdo

Perfil alterado.

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
get /v1/numeros/{id}/privacidade

Privacidade do número

Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do número.

Resposta 200 · OK

Privacidade atual.

CampoTipoObrig.Descrição
vistoPorUltimoenumsim

Valores: todos, contatos, contatos_exceto, ninguem.

fotoenumsim

Valores: todos, contatos, contatos_exceto, ninguem.

recadoenumsim

Valores: todos, contatos, contatos_exceto, ninguem.

adicionarGruposenumsim

Valores: todos, contatos, contatos_exceto, ninguem.

confirmacaoLeituraenumsim

Valores: todos, ninguem.

Exemplo de resposta
{
  "vistoPorUltimo": "todos",
  "foto": "todos",
  "recado": "todos",
  "adicionarGrupos": "todos",
  "confirmacaoLeitura": "todos"
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
put /v1/numeros/{id}/privacidade

Altera a privacidade do número

Só os campos enviados mudam. Auditado. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do número.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
vistoPorUltimoenumnão

Valores: todos, contatos, contatos_exceto, ninguem.

fotoenumnão

Valores: todos, contatos, contatos_exceto, ninguem.

recadoenumnão

Valores: todos, contatos, contatos_exceto, ninguem.

adicionarGruposenumnão

Valores: todos, contatos, contatos_exceto, ninguem.

confirmacaoLeituraenumnão

Valores: todos, ninguem.

Exemplo de corpo
{
  "vistoPorUltimo": "contatos"
}

Resposta 200 · OK

Privacidade depois da alteração.

CampoTipoObrig.Descrição
vistoPorUltimoenumsim

Valores: todos, contatos, contatos_exceto, ninguem.

fotoenumsim

Valores: todos, contatos, contatos_exceto, ninguem.

recadoenumsim

Valores: todos, contatos, contatos_exceto, ninguem.

adicionarGruposenumsim

Valores: todos, contatos, contatos_exceto, ninguem.

confirmacaoLeituraenumsim

Valores: todos, ninguem.

Exemplo de resposta
{
  "vistoPorUltimo": "todos",
  "foto": "todos",
  "recado": "todos",
  "adicionarGrupos": "todos",
  "confirmacaoLeitura": "todos"
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
get /v1/numeros/{id}/configuracoes

Configurações do número

Rejeitar chamadas, sempre online, ler automaticamente, ignorar grupos e status. Só NAO_OFICIAL (funciona com o número desconectado).

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do número.

Resposta 200 · OK

As configurações.

CampoTipoObrig.Descrição
rejeitarChamadasbooleansim
mensagemRejeicaostring | nullsim

Tamanho: 0 a 500.

sempreOnlinebooleansim

Aumenta o risco de banimento: comportamento não humano.

lerAutomaticamentebooleansim

Aumenta o risco de banimento: comportamento não humano.

ignorarGruposbooleansim
ignorarStatusbooleansim
Exemplo de resposta
{
  "rejeitarChamadas": false,
  "mensagemRejeicao": "texto",
  "sempreOnline": false,
  "lerAutomaticamente": false,
  "ignorarGrupos": false,
  "ignorarStatus": false
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
put /v1/numeros/{id}/configuracoes

Altera as configurações do número

Aplicadas no Evolution primeiro; se ele recusar ou estiver fora, 502 e nada muda. Reaplicadas quando o número reconecta. sempreOnline e lerAutomaticamente são comportamento não humano e aumentam o risco de banimento. Com rejeitarChamadas, as chamadas são recusadas sem gerar chamada.*. Auditado.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do número.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
rejeitarChamadasbooleannão
mensagemRejeicaostring | nullnão

Tamanho: 0 a 500.

sempreOnlinebooleannão

Aumenta o risco de banimento: comportamento não humano.

lerAutomaticamentebooleannão

Aumenta o risco de banimento: comportamento não humano.

ignorarGruposbooleannão
ignorarStatusbooleannão
Exemplo de corpo
{
  "rejeitarChamadas": true,
  "mensagemRejeicao": "Não atendemos ligações, mande mensagem"
}

Resposta 200 · OK

As configurações aplicadas.

CampoTipoObrig.Descrição
rejeitarChamadasbooleansim
mensagemRejeicaostring | nullsim

Tamanho: 0 a 500.

sempreOnlinebooleansim

Aumenta o risco de banimento: comportamento não humano.

lerAutomaticamentebooleansim

Aumenta o risco de banimento: comportamento não humano.

ignorarGruposbooleansim
ignorarStatusbooleansim
Exemplo de resposta
{
  "rejeitarChamadas": false,
  "mensagemRejeicao": "texto",
  "sempreOnline": false,
  "lerAutomaticamente": false,
  "ignorarGrupos": false,
  "ignorarStatus": false
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/numeros/{id}/historico/sincronizar

Pede mais histórico de uma conversa ao celular

Parte da mensagem mais antiga que o ZapHub tem da conversa (sem nenhuma, 422). O resultado chega em historico.sincronizado. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do número.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
telefonestringsim

Conversa a sincronizar. Tamanho: 8 a 30.

quantidadeintegernão

Intervalo: 1 a 100. Padrão: 50.

Exemplo de corpo
{
  "telefone": "5511987654321",
  "quantidade": 50
}

Resposta 202 · Aceito

Pedido enviado ao celular.

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/conversas/{id}/arquivar

Arquiva a conversa

Vale no WhatsApp do número; a conversa guarda o estado (arquivada, fixada, silenciadaAte). Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da conversa.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Resposta 200 · OK

A conversa.

CampoTipoObrig.Descrição
idstringsim
numeroIdstringsim
tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

contatoobjectsim
contato.telefonestringsim
contato.nomestring | nullsim
ultimaMensagemEmstringsim
ultimaEntradaEmstring | nullsim
janela24hobjectsim
janela24h.abertabooleansim
janela24h.expiraEmstring | nullsim
tipoenumsim

Em GRUPO, contato.telefone é o JID do grupo e contato.nome o nome dele. Valores: CONTATO, GRUPO.

etiquetasobject[]sim

Etiquetas do WhatsApp Business aplicadas (não oficial).

etiquetas[].idstringsim
etiquetas[].nomestringsim
etiquetas[].corintegersim
arquivadabooleansim
fixadabooleansim
silenciadaAtestring | nullsim

Fim do silêncio; muito no futuro = sempre.

Exemplo de resposta
{
  "id": "texto",
  "numeroId": "texto",
  "tipoNumero": "OFICIAL",
  "contato": {
    "telefone": "texto",
    "nome": "texto"
  },
  "ultimaMensagemEm": "texto",
  "ultimaEntradaEm": "texto",
  "janela24h": {
    "aberta": false,
    "expiraEm": "texto"
  },
  "tipo": "CONTATO",
  "etiquetas": [
    {
      "id": "texto",
      "nome": "texto",
      "cor": 0
    }
  ],
  "arquivada": false,
  "fixada": false,
  "silenciadaAte": "texto"
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/conversas/{id}/desarquivar

Desarquiva a conversa

Vale no WhatsApp do número; a conversa guarda o estado (arquivada, fixada, silenciadaAte). Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da conversa.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Resposta 200 · OK

A conversa.

CampoTipoObrig.Descrição
idstringsim
numeroIdstringsim
tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

contatoobjectsim
contato.telefonestringsim
contato.nomestring | nullsim
ultimaMensagemEmstringsim
ultimaEntradaEmstring | nullsim
janela24hobjectsim
janela24h.abertabooleansim
janela24h.expiraEmstring | nullsim
tipoenumsim

Em GRUPO, contato.telefone é o JID do grupo e contato.nome o nome dele. Valores: CONTATO, GRUPO.

etiquetasobject[]sim

Etiquetas do WhatsApp Business aplicadas (não oficial).

etiquetas[].idstringsim
etiquetas[].nomestringsim
etiquetas[].corintegersim
arquivadabooleansim
fixadabooleansim
silenciadaAtestring | nullsim

Fim do silêncio; muito no futuro = sempre.

Exemplo de resposta
{
  "id": "texto",
  "numeroId": "texto",
  "tipoNumero": "OFICIAL",
  "contato": {
    "telefone": "texto",
    "nome": "texto"
  },
  "ultimaMensagemEm": "texto",
  "ultimaEntradaEm": "texto",
  "janela24h": {
    "aberta": false,
    "expiraEm": "texto"
  },
  "tipo": "CONTATO",
  "etiquetas": [
    {
      "id": "texto",
      "nome": "texto",
      "cor": 0
    }
  ],
  "arquivada": false,
  "fixada": false,
  "silenciadaAte": "texto"
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/conversas/{id}/fixar

Fixa a conversa

Vale no WhatsApp do número; a conversa guarda o estado (arquivada, fixada, silenciadaAte). Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da conversa.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Resposta 200 · OK

A conversa.

CampoTipoObrig.Descrição
idstringsim
numeroIdstringsim
tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

contatoobjectsim
contato.telefonestringsim
contato.nomestring | nullsim
ultimaMensagemEmstringsim
ultimaEntradaEmstring | nullsim
janela24hobjectsim
janela24h.abertabooleansim
janela24h.expiraEmstring | nullsim
tipoenumsim

Em GRUPO, contato.telefone é o JID do grupo e contato.nome o nome dele. Valores: CONTATO, GRUPO.

etiquetasobject[]sim

Etiquetas do WhatsApp Business aplicadas (não oficial).

etiquetas[].idstringsim
etiquetas[].nomestringsim
etiquetas[].corintegersim
arquivadabooleansim
fixadabooleansim
silenciadaAtestring | nullsim

Fim do silêncio; muito no futuro = sempre.

Exemplo de resposta
{
  "id": "texto",
  "numeroId": "texto",
  "tipoNumero": "OFICIAL",
  "contato": {
    "telefone": "texto",
    "nome": "texto"
  },
  "ultimaMensagemEm": "texto",
  "ultimaEntradaEm": "texto",
  "janela24h": {
    "aberta": false,
    "expiraEm": "texto"
  },
  "tipo": "CONTATO",
  "etiquetas": [
    {
      "id": "texto",
      "nome": "texto",
      "cor": 0
    }
  ],
  "arquivada": false,
  "fixada": false,
  "silenciadaAte": "texto"
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/conversas/{id}/desafixar

Desafixa a conversa

Vale no WhatsApp do número; a conversa guarda o estado (arquivada, fixada, silenciadaAte). Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da conversa.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Resposta 200 · OK

A conversa.

CampoTipoObrig.Descrição
idstringsim
numeroIdstringsim
tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

contatoobjectsim
contato.telefonestringsim
contato.nomestring | nullsim
ultimaMensagemEmstringsim
ultimaEntradaEmstring | nullsim
janela24hobjectsim
janela24h.abertabooleansim
janela24h.expiraEmstring | nullsim
tipoenumsim

Em GRUPO, contato.telefone é o JID do grupo e contato.nome o nome dele. Valores: CONTATO, GRUPO.

etiquetasobject[]sim

Etiquetas do WhatsApp Business aplicadas (não oficial).

etiquetas[].idstringsim
etiquetas[].nomestringsim
etiquetas[].corintegersim
arquivadabooleansim
fixadabooleansim
silenciadaAtestring | nullsim

Fim do silêncio; muito no futuro = sempre.

Exemplo de resposta
{
  "id": "texto",
  "numeroId": "texto",
  "tipoNumero": "OFICIAL",
  "contato": {
    "telefone": "texto",
    "nome": "texto"
  },
  "ultimaMensagemEm": "texto",
  "ultimaEntradaEm": "texto",
  "janela24h": {
    "aberta": false,
    "expiraEm": "texto"
  },
  "tipo": "CONTATO",
  "etiquetas": [
    {
      "id": "texto",
      "nome": "texto",
      "cor": 0
    }
  ],
  "arquivada": false,
  "fixada": false,
  "silenciadaAte": "texto"
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/conversas/{id}/silenciar

Silencia a conversa

Vale no WhatsApp do número; a conversa guarda o estado (arquivada, fixada, silenciadaAte). Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da conversa.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
duracaoenumsim

Valores: 8h, 1semana, sempre.

Exemplo de corpo
{
  "duracao": "8h"
}

Resposta 200 · OK

A conversa.

CampoTipoObrig.Descrição
idstringsim
numeroIdstringsim
tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

contatoobjectsim
contato.telefonestringsim
contato.nomestring | nullsim
ultimaMensagemEmstringsim
ultimaEntradaEmstring | nullsim
janela24hobjectsim
janela24h.abertabooleansim
janela24h.expiraEmstring | nullsim
tipoenumsim

Em GRUPO, contato.telefone é o JID do grupo e contato.nome o nome dele. Valores: CONTATO, GRUPO.

etiquetasobject[]sim

Etiquetas do WhatsApp Business aplicadas (não oficial).

etiquetas[].idstringsim
etiquetas[].nomestringsim
etiquetas[].corintegersim
arquivadabooleansim
fixadabooleansim
silenciadaAtestring | nullsim

Fim do silêncio; muito no futuro = sempre.

Exemplo de resposta
{
  "id": "texto",
  "numeroId": "texto",
  "tipoNumero": "OFICIAL",
  "contato": {
    "telefone": "texto",
    "nome": "texto"
  },
  "ultimaMensagemEm": "texto",
  "ultimaEntradaEm": "texto",
  "janela24h": {
    "aberta": false,
    "expiraEm": "texto"
  },
  "tipo": "CONTATO",
  "etiquetas": [
    {
      "id": "texto",
      "nome": "texto",
      "cor": 0
    }
  ],
  "arquivada": false,
  "fixada": false,
  "silenciadaAte": "texto"
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/conversas/{id}/dessilenciar

Tira o silêncio da conversa

Vale no WhatsApp do número; a conversa guarda o estado (arquivada, fixada, silenciadaAte). Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da conversa.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Resposta 200 · OK

A conversa.

CampoTipoObrig.Descrição
idstringsim
numeroIdstringsim
tipoNumeroenumsim

Valores: OFICIAL, NAO_OFICIAL.

contatoobjectsim
contato.telefonestringsim
contato.nomestring | nullsim
ultimaMensagemEmstringsim
ultimaEntradaEmstring | nullsim
janela24hobjectsim
janela24h.abertabooleansim
janela24h.expiraEmstring | nullsim
tipoenumsim

Em GRUPO, contato.telefone é o JID do grupo e contato.nome o nome dele. Valores: CONTATO, GRUPO.

etiquetasobject[]sim

Etiquetas do WhatsApp Business aplicadas (não oficial).

etiquetas[].idstringsim
etiquetas[].nomestringsim
etiquetas[].corintegersim
arquivadabooleansim
fixadabooleansim
silenciadaAtestring | nullsim

Fim do silêncio; muito no futuro = sempre.

Exemplo de resposta
{
  "id": "texto",
  "numeroId": "texto",
  "tipoNumero": "OFICIAL",
  "contato": {
    "telefone": "texto",
    "nome": "texto"
  },
  "ultimaMensagemEm": "texto",
  "ultimaEntradaEm": "texto",
  "janela24h": {
    "aberta": false,
    "expiraEm": "texto"
  },
  "tipo": "CONTATO",
  "etiquetas": [
    {
      "id": "texto",
      "nome": "texto",
      "cor": 0
    }
  ],
  "arquivada": false,
  "fixada": false,
  "silenciadaAte": "texto"
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/chamadas/{chamadaId}/rejeitar

Rejeita uma chamada recebida

Use o chamadaId de chamada.recebida. Chamada já encerrada → 409. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel; desconectado → 409). Falha do WhatsApp → 502, sem alterar nada.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
chamadaIdstringsim

chamadaId do evento chamada.recebida.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros). Tamanho: 1 a 64.

Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001"
}

Resposta 204 · Sem conteúdo

Chamada rejeitada.

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/numeros/{id}/status

Publica no status (stories) do número

Só NAO_OFICIAL. Texto puro (o Evolution Go não aplica cor de fundo nem fonte) ou imagem/vídeo por URL HTTPS pública ou base64 (via armazenamento). Aparece para os contatos, conforme a privacidade de status do número. Limite de 30 por número em 24h (configurável na plataforma); acima, 429 com Retry-After. Não entra na caixa de entrada nem na economia.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do número.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
tipoenumsim

Valores: texto, midia.

textostringnão

Só em texto. O Evolution Go publica o texto sem cor de fundo nem fonte. Tamanho: 1 a 700.

midiaobjectnão

Só em midia: url ou base64 (com mimeType).

midia.tipoenumsim

Valores: imagem, video.

midia.urlstringnão

URL HTTPS pública. Tamanho: 0 a 2048.

midia.base64stringnão

Tamanho: 0 a 23000000.

midia.mimeTypestringnão

Tamanho: 0 a 100.

legendastringnão

Tamanho: 0 a 700.

Exemplo de corpo
{
  "tipo": "midia",
  "midia": {
    "tipo": "imagem",
    "url": "https://cdn.exemplo.com.br/promo.jpg"
  },
  "legenda": "Só hoje: frete grátis"
}

Resposta 202 · Aceito

Publicado.

CampoTipoObrig.Descrição
restanteHojeintegersim
Exemplo de resposta
{
  "restanteHoje": 29
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
get /v1/numeros/{id}/etiquetas

Etiquetas do número

Sincroniza com o WhatsApp e lista. Só números NAO_OFICIAL com WhatsApp Business (sem Business → 422 recurso_indisponivel).

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do número.

Resposta 200 · OK

As etiquetas.

CampoTipoObrig.Descrição
etiquetasobject[]sim
etiquetas[].idstringsim
etiquetas[].nomestringsim
etiquetas[].corintegersim

Índice da cor do WhatsApp (0 a 19).

Exemplo de resposta
{
  "etiquetas": [
    {
      "id": "clx0etiqueta0000000000001",
      "nome": "Pagamento pendente",
      "cor": 3
    }
  ]
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/numeros/{id}/etiquetas

Cria ou edita uma etiqueta

Sem etiquetaId, cria. Emite etiqueta.alterada quando o WhatsApp confirma. Só números NAO_OFICIAL com WhatsApp Business (sem Business → 422 recurso_indisponivel).

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do número.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
etiquetaIdstringnão

Para editar uma existente; sem ele, cria. Tamanho: 1 a 64.

nomestringsim

Tamanho: 1 a 100.

corintegernão

Intervalo: 0 a 19. Padrão: 0.

Exemplo de corpo
{
  "nome": "Pagamento pendente",
  "cor": 3
}

Resposta 200 · OK

A etiqueta.

CampoTipoObrig.Descrição
idstringsim
nomestringsim
corintegersim

Índice da cor do WhatsApp (0 a 19).

Exemplo de resposta
{
  "id": "clx0etiqueta0000000000001",
  "nome": "Pagamento pendente",
  "cor": 3
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
delete /v1/numeros/{id}/etiquetas/{etiquetaId}

Remove uma etiqueta

Também some das conversas. Só números NAO_OFICIAL com WhatsApp Business (sem Business → 422 recurso_indisponivel).

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do número.

etiquetaIdstringsim

Identificador da etiqueta.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Resposta 204 · Sem conteúdo

Removida.

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/conversas/{id}/etiquetas

Aplica ou tira uma etiqueta da conversa

As etiquetas da conversa aparecem em GET /v1/conversas (conversa.etiquetas). Só números NAO_OFICIAL com WhatsApp Business (sem Business → 422 recurso_indisponivel).

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da conversa.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
etiquetaIdstringsim

Tamanho: 1 a 64.

acaoenumsim

Valores: aplicar, remover.

Exemplo de corpo
{
  "etiquetaId": "clx0etiqueta0000000000001",
  "acao": "aplicar"
}

Resposta 204 · Sem conteúdo

Feito.

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/mensagens/{id}/etiquetas

Aplica ou tira uma etiqueta da mensagem

Só números NAO_OFICIAL com WhatsApp Business (sem Business → 422 recurso_indisponivel).

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da mensagem.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
etiquetaIdstringsim

Tamanho: 1 a 64.

acaoenumsim

Valores: aplicar, remover.

Exemplo de corpo
{
  "etiquetaId": "clx0etiqueta0000000000001",
  "acao": "aplicar"
}

Resposta 204 · Sem conteúdo

Feito.

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.

Grupos

Números não oficiais: listar, criar e administrar grupos, convites, entrar e sair. Para mandar mensagem ao grupo, use grupoId nos envios. Receber mensagens de grupo exige ignorarGrupos: false nas configurações do número. Grupos não geram lead, não disparam resposta automática nem opt-out e não entram na economia.

get /v1/numeros/{id}/grupos

Grupos do número

Lidos do WhatsApp na hora; o ZapHub guarda nome, configurações e se o número é admin. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel). Falha do WhatsApp → 502.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do número.

Resposta 200 · OK

Os grupos.

CampoTipoObrig.Descrição
gruposobject[]sim
grupos[].idstringsim
grupos[].numeroIdstringsim
grupos[].nomestring | nullsim
grupos[].descricaostring | nullsim
grupos[].soAdminsEnviambooleansim
grupos[].soAdminsEditambooleansim
grupos[].numeroEhAdminbooleansim
grupos[].participantesobject[]não

Só em GET /v1/grupos/{id} (lido do WhatsApp, cache de 1 minuto).

grupos[].participantes[].telefonestring | nullsim

Nulo quando o WhatsApp só informa o identificador privado.

grupos[].participantes[].adminbooleansim
grupos[].participantes[].superAdminbooleansim
grupos[].totalParticipantesintegernão
Exemplo de resposta
{
  "grupos": [
    {
      "id": "clx0grupo00000000000000001",
      "numeroId": "clx0numero000000000000001",
      "nome": "Clientes VIP",
      "descricao": "Ofertas da semana",
      "soAdminsEnviam": false,
      "soAdminsEditam": true,
      "numeroEhAdmin": true
    }
  ]
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/grupos

Cria um grupo

Até 50 participantes por chamada. Limites por número em 24h (padrão: 10 grupos e 200 participantes adicionados); acima, 429 com Retry-After. Criar grupos e adicionar pessoas em massa é sinal de spam para o WhatsApp. Auditado. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel). Falha do WhatsApp → 502.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Número NAO_OFICIAL conectado. Tamanho: 1 a 64.

nomestringsim

Tamanho: 1 a 100.

participantesstring[]sim

No máximo 50 itens.

Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001",
  "nome": "Clientes VIP",
  "participantes": [
    "5511987654321"
  ]
}

Resposta 201 · Criado

O grupo criado.

CampoTipoObrig.Descrição
idstringsim
numeroIdstringsim
nomestring | nullsim
descricaostring | nullsim
soAdminsEnviambooleansim
soAdminsEditambooleansim
numeroEhAdminbooleansim
participantesobject[]não

Só em GET /v1/grupos/{id} (lido do WhatsApp, cache de 1 minuto).

participantes[].telefonestring | nullsim

Nulo quando o WhatsApp só informa o identificador privado.

participantes[].adminbooleansim
participantes[].superAdminbooleansim
totalParticipantesintegernão
Exemplo de resposta
{
  "id": "clx0grupo00000000000000001",
  "numeroId": "clx0numero000000000000001",
  "nome": "Clientes VIP",
  "descricao": "Ofertas da semana",
  "soAdminsEnviam": false,
  "soAdminsEditam": true,
  "numeroEhAdmin": true
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/grupos/entrar

Entra num grupo por link de convite

O grupo aparece em grupo.entrou e na próxima listagem. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel). Falha do WhatsApp → 502.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros). Tamanho: 1 a 64.

linkstringsim
Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001",
  "link": "https://chat.whatsapp.com/AbCdEfGhIjKlMnOpQrStUv"
}

Resposta 202 · Aceito

Pedido enviado ao WhatsApp.

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
get /v1/grupos/{id}

Detalhe do grupo com participantes

Participantes lidos do WhatsApp (cache de 1 minuto). Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel). Falha do WhatsApp → 502.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do grupo (id de GET /v1/numeros/{id}/grupos).

Resposta 200 · OK

O grupo.

CampoTipoObrig.Descrição
idstringsim
numeroIdstringsim
nomestring | nullsim
descricaostring | nullsim
soAdminsEnviambooleansim
soAdminsEditambooleansim
numeroEhAdminbooleansim
participantesobject[]não

Só em GET /v1/grupos/{id} (lido do WhatsApp, cache de 1 minuto).

participantes[].telefonestring | nullsim

Nulo quando o WhatsApp só informa o identificador privado.

participantes[].adminbooleansim
participantes[].superAdminbooleansim
totalParticipantesintegernão
Exemplo de resposta
{
  "id": "texto",
  "numeroId": "texto",
  "nome": "texto",
  "descricao": "texto",
  "soAdminsEnviam": false,
  "soAdminsEditam": false,
  "numeroEhAdmin": false,
  "participantes": [
    {
      "telefone": "texto",
      "admin": false,
      "superAdmin": false
    }
  ],
  "totalParticipantes": 0
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
patch /v1/grupos/{id}

Edita nome, descrição, foto e configurações

Com "só admins editam", nome, descrição e foto exigem que o número seja admin; as configurações sempre exigem (422). Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel). Falha do WhatsApp → 502.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do grupo (id de GET /v1/numeros/{id}/grupos).

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
nomestringnão

Tamanho: 1 a 100.

descricaostringnão

Tamanho: 0 a 2048.

fotostringnão

JPEG ou PNG: URL HTTPS pública ou base64. Tamanho: 0 a 7000000.

soAdminsEnviambooleannão
soAdminsEditambooleannão
Exemplo de corpo
{
  "descricao": "Ofertas da semana",
  "soAdminsEnviam": true
}

Resposta 200 · OK

O grupo.

CampoTipoObrig.Descrição
idstringsim
numeroIdstringsim
nomestring | nullsim
descricaostring | nullsim
soAdminsEnviambooleansim
soAdminsEditambooleansim
numeroEhAdminbooleansim
participantesobject[]não

Só em GET /v1/grupos/{id} (lido do WhatsApp, cache de 1 minuto).

participantes[].telefonestring | nullsim

Nulo quando o WhatsApp só informa o identificador privado.

participantes[].adminbooleansim
participantes[].superAdminbooleansim
totalParticipantesintegernão
Exemplo de resposta
{
  "id": "clx0grupo00000000000000001",
  "numeroId": "clx0numero000000000000001",
  "nome": "Clientes VIP",
  "descricao": "Ofertas da semana",
  "soAdminsEnviam": false,
  "soAdminsEditam": true,
  "numeroEhAdmin": true
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/grupos/{id}/participantes

Adiciona, remove, promove ou rebaixa participantes

Só se o número for admin (422). Adicionar conta no limite diário de participantes (429). Auditado. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel). Falha do WhatsApp → 502.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do grupo (id de GET /v1/numeros/{id}/grupos).

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
acaoenumsim

Valores: adicionar, remover, promover, rebaixar.

telefonesstring[]sim

No máximo 50 itens.

Exemplo de corpo
{
  "acao": "adicionar",
  "telefones": [
    "5511987654321"
  ]
}

Resposta 204 · Sem conteúdo

Feito.

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
get /v1/grupos/{id}/convite

Link de convite do grupo

Só admins. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel). Falha do WhatsApp → 502.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do grupo (id de GET /v1/numeros/{id}/grupos).

Resposta 200 · OK

O link.

CampoTipoObrig.Descrição
linkstringsim
Exemplo de resposta
{
  "link": "https://chat.whatsapp.com/AbCdEfGhIjKlMnOpQrStUv"
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/grupos/{id}/convite/revogar

Revoga o link de convite e gera outro

Só admins. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel). Falha do WhatsApp → 502.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do grupo (id de GET /v1/numeros/{id}/grupos).

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Resposta 200 · OK

O link novo.

CampoTipoObrig.Descrição
linkstringsim
Exemplo de resposta
{
  "link": "texto"
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
delete /v1/grupos/{id}/participacao

O número sai do grupo

Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel). Falha do WhatsApp → 502.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do grupo (id de GET /v1/numeros/{id}/grupos).

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Resposta 204 · Sem conteúdo

Saiu.

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.

Comunidades e canais

Números não oficiais: criar comunidades e juntar grupos a elas; criar canais (newsletter), listar os que o número segue, ver detalhes, abrir um link de convite e ler as publicações. Nada disso é guardado no ZapHub. Seguir um canal pelo link não está disponível (o Evolution não tem essa ação).

post /v1/comunidades

Cria uma comunidade

Conta no limite diário de grupos criados por número (429 com Retry-After). Auditado. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel).

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros). Tamanho: 1 a 64.

nomestringsim

Tamanho: 1 a 100.

Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001",
  "nome": "Moradores do bairro"
}

Resposta 201 · Criado

A comunidade criada.

CampoTipoObrig.Descrição
idstringsim

JID da comunidade no WhatsApp (...@g.us).

nomestringsim
Exemplo de resposta
{
  "id": "120363000000000009@g.us",
  "nome": "Moradores do bairro"
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/comunidades/{comunidadeId}/grupos

Adiciona ou remove grupos da comunidade

grupoIds são os id de GET /v1/numeros/{id}/grupos; o número precisa ser admin dos grupos. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel).

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
comunidadeIdstringsim

id da comunidade (termina em @g.us).

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros). Tamanho: 1 a 64.

acaoenumsim

Valores: adicionar, remover.

grupoIdsstring[]sim

id dos grupos (de GET /v1/numeros/{id}/grupos). No máximo 50 itens.

Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001",
  "acao": "adicionar",
  "grupoIds": [
    "clx0grupo000000000000001"
  ]
}

Resposta 204 · Sem conteúdo

Feito.

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
get /v1/numeros/{id}/canais

Canais que o número segue ou administra

Consulta o WhatsApp a cada chamada. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel).

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do número.

Resposta 200 · OK

Os canais.

CampoTipoObrig.Descrição
canaisobject[]sim
canais[].idstringsim

JID do canal (...@newsletter).

canais[].nomestring | nullsim
canais[].descricaostring | nullsim
canais[].seguidoresinteger | nullsim
canais[].convitestring | nullsim

Link de convite do canal.

canais[].papelstring | nullsim

Papel do número no canal (owner, admin, subscriber…).

Exemplo de resposta
{
  "canais": [
    {
      "id": "120363100000000001@newsletter",
      "nome": "Ofertas da Loja",
      "descricao": "Novidades toda semana",
      "seguidores": 42,
      "convite": "https://whatsapp.com/channel/0029VaExemplo123456",
      "papel": "owner"
    }
  ]
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/numeros/{id}/canais

Cria um canal

Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel).

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do número.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
nomestringsim

Tamanho: 1 a 100.

descricaostringnão

Tamanho: 0 a 2048.

Exemplo de corpo
{
  "nome": "Ofertas da Loja",
  "descricao": "Novidades toda semana"
}

Resposta 201 · Criado

O canal criado.

CampoTipoObrig.Descrição
idstringsim

JID do canal (...@newsletter).

nomestring | nullsim
descricaostring | nullsim
seguidoresinteger | nullsim
convitestring | nullsim

Link de convite do canal.

papelstring | nullsim

Papel do número no canal (owner, admin, subscriber…).

Exemplo de resposta
{
  "id": "120363100000000001@newsletter",
  "nome": "Ofertas da Loja",
  "descricao": "Novidades toda semana",
  "seguidores": 42,
  "convite": "https://whatsapp.com/channel/0029VaExemplo123456",
  "papel": "owner"
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
post /v1/canais/convite

Abre um link de convite de canal

Devolve os dados do canal sem segui-lo. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel).

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros). Tamanho: 1 a 64.

linkstringsim
Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001",
  "link": "https://whatsapp.com/channel/0029VaExemplo123456"
}

Resposta 200 · OK

O canal.

CampoTipoObrig.Descrição
idstringsim

JID do canal (...@newsletter).

nomestring | nullsim
descricaostring | nullsim
seguidoresinteger | nullsim
convitestring | nullsim

Link de convite do canal.

papelstring | nullsim

Papel do número no canal (owner, admin, subscriber…).

Exemplo de resposta
{
  "id": "120363100000000001@newsletter",
  "nome": "Ofertas da Loja",
  "descricao": "Novidades toda semana",
  "seguidores": 42,
  "convite": "https://whatsapp.com/channel/0029VaExemplo123456",
  "papel": null
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
get /v1/canais/{canalId}

Detalhes de um canal

Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel).

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
canalIdstringsim

id do canal (termina em @newsletter).

Parâmetros de consulta (query string)

ParâmetroTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros).

Resposta 200 · OK

O canal.

CampoTipoObrig.Descrição
idstringsim

JID do canal (...@newsletter).

nomestring | nullsim
descricaostring | nullsim
seguidoresinteger | nullsim
convitestring | nullsim

Link de convite do canal.

papelstring | nullsim

Papel do número no canal (owner, admin, subscriber…).

Exemplo de resposta
{
  "id": "120363100000000001@newsletter",
  "nome": "Ofertas da Loja",
  "descricao": "Novidades toda semana",
  "seguidores": 42,
  "convite": "https://whatsapp.com/channel/0029VaExemplo123456",
  "papel": "owner"
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.
get /v1/canais/{canalId}/mensagens

Publicações do canal

Da mais nova para a mais antiga; para a página seguinte, passe o antesDe da resposta. Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel).

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
canalIdstringsim

id do canal (termina em @newsletter).

Parâmetros de consulta (query string)

ParâmetroTipoObrig.Descrição
numeroIdstringsim

Identificador público do número (GET /v1/numeros).

quantidadeintegernão
antesDeintegernão

servidorId da mensagem mais antiga da página anterior.

Resposta 200 · OK

As publicações.

CampoTipoObrig.Descrição
mensagensobject[]sim
mensagens[].idstringsim
mensagens[].servidorIdintegersim
mensagens[].tipostringsim
mensagens[].textostring | nullsim
mensagens[].enviadaEmstring | nullsim
mensagens[].visualizacoesintegersim
mensagens[].reacoesobjectsim
antesDeinteger | nullsim

Passe em antesDe para a página seguinte (mais antiga).

Exemplo de resposta
{
  "mensagens": [
    {
      "id": "3EB0A1B2C3D4",
      "servidorId": 120,
      "tipo": "text",
      "texto": "Promoção relâmpago hoje!",
      "enviadaEm": "2026-11-01T12:00:00.000Z",
      "visualizacoes": 310,
      "reacoes": {
        "👍": 12
      }
    }
  ],
  "antesDe": 120
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
  • 502 : O WhatsApp (Evolution ou Meta) recusou a operação. O `code` e a mensagem são os do canal; nada foi alterado.

Templates e campanhas

Templates aprovados do número oficial e campanhas em massa (somente números OFICIAL).

Exemplo: Listar templates e criar uma campanha com contatos inline

Informe até 50.000 contatos, cada um com as variáveis do template na ordem. Para usar uma lista do painel, troque contatos por listaId.

curl https://api.zaphub.wssengenharia.com.br/v1/numeros/clx0oficial00000000000001/templates -H "Authorization: Bearer $ZAPHUB_KEY"

curl -X POST https://api.zaphub.wssengenharia.com.br/v1/campanhas \
  -H "Authorization: Bearer $ZAPHUB_KEY" -H "Idempotency-Key: promo-outubro" -H "Content-Type: application/json" \
  -d '{
    "nome": "Promo de outubro",
    "numeroId": "clx0oficial00000000000001",
    "templateId": "clx0template0000000000001",
    "contatos": [
      {"telefone": "11987654321", "nome": "Ana",   "variaveis": ["Ana", "COD-1042"]},
      {"telefone": "11987654322", "nome": "Bruno", "variaveis": ["Bruno", "COD-1043"]}
    ]
  }'

# 202 -> { "id": "...", "status": "EM_ANDAMENTO", "custoEstimadoCentavos": 68, "estimativa": { ... } }
import { zaphub } from "./zaphub.js";

const { templates } = await zaphub("GET", "/v1/numeros/clx0oficial00000000000001/templates");
const template = templates.find((t) => t.nome === "promo_outubro" && t.status === "APPROVED");

const campanha = await zaphub(
  "POST",
  "/v1/campanhas",
  {
    nome: "Promo de outubro",
    numeroId: "clx0oficial00000000000001",
    templateId: template.id,
    contatos: clientes.map((c) => ({ telefone: c.telefone, nome: c.nome, variaveis: [c.nome, c.codigo] })),
  },
  { "Idempotency-Key": "promo-outubro" },
);
console.log(campanha.id, campanha.estimativa.centavos, campanha.contatos.rejeitados);
<?php
require 'zaphub.php';

[, $lista] = zaphub('GET', '/v1/numeros/clx0oficial00000000000001/templates');
$template = array_values(array_filter($lista['templates'], fn ($t) => $t['nome'] === 'promo_outubro' && $t['status'] === 'APPROVED'))[0];

[, $campanha] = zaphub('POST', '/v1/campanhas', [
    'nome' => 'Promo de outubro',
    'numeroId' => 'clx0oficial00000000000001',
    'templateId' => $template['id'],
    'contatos' => array_map(fn ($c) => ['telefone' => $c['telefone'], 'nome' => $c['nome'], 'variaveis' => [$c['nome'], $c['codigo']]], $clientes),
], ['Idempotency-Key: promo-outubro']);
echo $campanha['id'] . ' ' . $campanha['estimativa']['centavos'];

Exemplo: Acompanhar, pausar, retomar e cancelar

curl https://api.zaphub.wssengenharia.com.br/v1/campanhas/clx0campanha00000000000001 -H "Authorization: Bearer $ZAPHUB_KEY"
curl -X POST https://api.zaphub.wssengenharia.com.br/v1/campanhas/clx0campanha00000000000001/pausar  -H "Authorization: Bearer $ZAPHUB_KEY"
curl -X POST https://api.zaphub.wssengenharia.com.br/v1/campanhas/clx0campanha00000000000001/retomar -H "Authorization: Bearer $ZAPHUB_KEY"
curl -X POST https://api.zaphub.wssengenharia.com.br/v1/campanhas/clx0campanha00000000000001/cancelar -H "Authorization: Bearer $ZAPHUB_KEY"
import { zaphub } from "./zaphub.js";

const id = "clx0campanha00000000000001";
const c = await zaphub("GET", "/v1/campanhas/" + id);
console.log(c.status, c.totais, c.taxaFalha);   // ou receba o evento campanha.status
await zaphub("POST", "/v1/campanhas/" + id + "/pausar");
await zaphub("POST", "/v1/campanhas/" + id + "/retomar");
await zaphub("POST", "/v1/campanhas/" + id + "/cancelar");
<?php
require 'zaphub.php';

$id = 'clx0campanha00000000000001';
[, $c] = zaphub('GET', "/v1/campanhas/$id");
echo $c['status'] . ' ' . json_encode($c['totais']);   // ou receba o evento campanha.status
zaphub('POST', "/v1/campanhas/$id/pausar");
zaphub('POST', "/v1/campanhas/$id/retomar");
zaphub('POST', "/v1/campanhas/$id/cancelar");
get /v1/numeros/{id}/templates

Templates do número oficial

Somente OFICIAL. Cada template traz o id para usar em templateId, o status de aprovação na Meta e a quantidade de variáveis.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador público do número OFICIAL.

Resposta 200 · OK

Templates sincronizados da Meta.

CampoTipoObrig.Descrição
templatesobject[]sim
templates[].idstringsim

Use em templateId (mensagens e campanhas).

templates[].nomestringsim
templates[].idiomastringsim
templates[].categoriaenumsim

Valores: MARKETING, UTILITY, AUTHENTICATION.

templates[].statusstringsim

Status na Meta: APPROVED, PENDING, REJECTED, PAUSED, DISABLED ou INDISPONIVEL.

templates[].motivostring | nullsim
templates[].totalVariaveisintegersim
templates[].variaveisobject[]sim
templates[].sincronizadoEmstringsim
Exemplo de resposta
{
  "templates": [
    {
      "id": "texto",
      "nome": "texto",
      "idioma": "texto",
      "categoria": "MARKETING",
      "status": "texto",
      "motivo": "texto",
      "totalVariaveis": 0,
      "variaveis": [
        {
          "chave": null
        }
      ],
      "sincronizadoEm": "texto"
    }
  ]
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
get /v1/campanhas

Lista as campanhas

Parâmetros de consulta (query string)

ParâmetroTipoObrig.Descrição
statusstringnão

Valores: RASCUNHO, AGENDADA, EM_ANDAMENTO, PAUSADA, PAUSADA_AUTOMATICAMENTE, CONCLUIDA, CANCELADA.

Resposta 200 · OK

Campanhas da conta (até 200, mais recentes primeiro).

CampoTipoObrig.Descrição
campanhasobject[]sim
campanhas[].idstringsim
campanhas[].nomestringsim
campanhas[].statusenumsim

Valores: RASCUNHO, AGENDADA, EM_ANDAMENTO, PAUSADA, PAUSADA_AUTOMATICAMENTE, CONCLUIDA, CANCELADA.

campanhas[].numeroobjectsim
campanhas[].numero.idstringsim
campanhas[].numero.apelidostringsim
campanhas[].templateobjectsim
campanhas[].template.idstringsim
campanhas[].template.nomestringsim
campanhas[].agendadaParastring | nullsim
campanhas[].iniciadaEmstring | nullsim
campanhas[].concluidaEmstring | nullsim
campanhas[].custoEstimadoCentavosintegersim
campanhas[].totalintegersim
campanhas[].totaisobjectsim
campanhas[].totais.pendenteintegersim
campanhas[].totais.enviadointegersim
campanhas[].totais.entregueintegersim
campanhas[].totais.lidointegersim
campanhas[].totais.respondidointegersim
campanhas[].totais.falhouintegersim
campanhas[].totais.ignoradoOptoutintegersim
campanhas[].criadoEmstringsim
Exemplo de resposta
{
  "campanhas": [
    {
      "id": "texto",
      "nome": "texto",
      "status": "RASCUNHO",
      "numero": {
        "id": "texto",
        "apelido": "texto"
      },
      "template": {
        "id": "texto",
        "nome": "texto"
      },
      "agendadaPara": "texto",
      "iniciadaEm": "texto",
      "concluidaEm": "texto",
      "custoEstimadoCentavos": 0,
      "total": 0,
      "totais": {
        "pendente": 0,
        "enviado": 0,
        "entregue": 0,
        "lido": 0,
        "respondido": 0,
        "falhou": 0,
        "ignoradoOptout": 0
      },
      "criadoEm": "texto"
    }
  ]
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/campanhas

Cria uma campanha (número oficial)

Somente números OFICIAL (em NAO_OFICIAL: 422 campanha_exige_api_oficial). Informe os contatos inline (até 50.000, cada um com suas variaveis) ou uma listaId. Responde 202 com o id e a estimativa de custo; a campanha já sai disparando (use agendadaPara para agendar ou iniciar: false para criar como rascunho). Cada mudança de estado emite campanha.status.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
nomestringsim

Tamanho: 1 a 120.

numeroIdstringsim

Número OFICIAL. Tamanho: 1 a 64.

templateIdstringsim

id do template em GET /v1/numeros/{id}/templates. Tamanho: 1 a 64.

listaIdstringnão

Lista de contatos já criada no painel. Use listaId OU contatos. Tamanho: 1 a 64.

contatosobject[]não

Contatos inline (até 50.000), cada um com suas variáveis. No máximo 50.000 itens.

contatos[].telefonestring | numbersim

Telefone com DDD (e DDI se não for do Brasil, com +).

contatos[].nomestring | nullnão

Tamanho: 0 a 200.

contatos[].variaveisstring | number[]não

Valores das variáveis do template para este contato, na ordem ({{1}}, {{2}}…). Dispensa o mapeamento. No máximo 20 itens.

contatos[].camposobjectnão

Campos extras, referenciáveis no mapeamento.

mapeamentoobjectnão

Posição da variável ("1") → campo do contato ou valor fixo. Omita quando os contatos trazem variaveis.

mapeamento{chave}.tipoenumsim

Valores: campo.

mapeamento{chave}.valorstringsim

Tamanho: 1 a 100.

agendadaParastringnão

Sem fuso explícito vale o horário de Brasília.

pausaAutomaticabooleannão

Padrão: false.

iniciarbooleannão

true (padrão) dispara agora; ignorado se houver agendadaPara. false cria como rascunho. Padrão: true.

Exemplo de corpo
{
  "nome": "Promo de outubro",
  "numeroId": "clx0numero000000000000001",
  "templateId": "clx0template0000000000001",
  "contatos": [
    {
      "telefone": "11987654321",
      "nome": "Ana",
      "variaveis": [
        "Ana",
        "COD-1042"
      ]
    },
    {
      "telefone": "11987654322",
      "nome": "Bruno",
      "variaveis": [
        "Bruno",
        "COD-1043"
      ]
    }
  ]
}

Resposta 202 · Aceito

Campanha criada.

CampoTipoObrig.Descrição
idstringsim
nomestringsim
statusenumsim

Valores: RASCUNHO, AGENDADA, EM_ANDAMENTO, PAUSADA, PAUSADA_AUTOMATICAMENTE, CONCLUIDA, CANCELADA.

numeroobjectsim
numero.idstringsim
numero.apelidostringsim
templateobjectsim
template.idstringsim
template.nomestringsim
listaobject | nullsim
lista.idstringsim
lista.nomestringsim
agendadaParastring | nullsim
iniciadaEmstring | nullsim
concluidaEmstring | nullsim
pausaAutomaticabooleansim
motivoPausastring | nullsim
custoEstimadoCentavosintegersim
totalintegersim

Contatos da campanha.

totaisobjectsim
totais.pendenteintegersim
totais.enviadointegersim
totais.entregueintegersim
totais.lidointegersim
totais.respondidointegersim
totais.falhouintegersim
totais.ignoradoOptoutintegersim
alertasobject[]sim

Informativos; nunca bloqueiam.

alertas[].codigostringsim
alertas[].mensagemstringsim
taxaFalhanumbersim

Falhas ÷ (enviados + falhas), em %.

erroMaisFrequenteobject | nullsim
erroMaisFrequente.codigostringsim
erroMaisFrequente.mensagemstringsim
erroMaisFrequente.quantidadeintegersim
criadoEmstringsim
estimativaobjectsim
estimativa.categoriaenumsim

Valores: MARKETING, UTILITY, AUTHENTICATION.

estimativa.quantidadeintegersim

Contatos que receberão (exclui opt-out).

estimativa.ignoradosOptoutintegersim
estimativa.centavosintegersim

Estimativa do que a Meta cobra, em centavos de BRL.

estimativa.porPaisobject[]sim
estimativa.porPais[].ddistringsim
estimativa.porPais[].rotulostringsim
estimativa.porPais[].quantidadeintegersim
estimativa.porPais[].precoUsdstringsim
estimativa.porPais[].centavosPorMensagemintegersim
estimativa.porPais[].centavosintegersim
estimativa.cambiostring | nullsim
estimativa.tabelaVersaointeger | nullsim
estimativa.semPrecostring[]sim
estimativa.estimadoenumsim

Valores: true.

contatosobject | nullsim

Resumo da importação quando a campanha foi criada com contatos inline.

contatos.recebidosintegersim
contatos.aceitosintegersim
contatos.rejeitadosintegersim
contatos.duplicadosintegersim
contatos.rejeitadasAmostraobject[]sim
contatos.rejeitadasAmostra[].linhaintegersim
contatos.rejeitadasAmostra[].telefonestringsim
contatos.rejeitadasAmostra[].motivostringsim
Exemplo de resposta
{
  "id": "texto",
  "nome": "texto",
  "status": "RASCUNHO",
  "numero": {
    "id": "texto",
    "apelido": "texto"
  },
  "template": {
    "id": "texto",
    "nome": "texto"
  },
  "lista": {
    "id": "texto",
    "nome": "texto"
  },
  "agendadaPara": "texto",
  "iniciadaEm": "texto",
  "concluidaEm": "texto",
  "pausaAutomatica": false,
  "motivoPausa": "texto",
  "custoEstimadoCentavos": 0,
  "total": 0,
  "totais": {
    "pendente": 0,
    "enviado": 0,
    "entregue": 0,
    "lido": 0,
    "respondido": 0,
    "falhou": 0,
    "ignoradoOptout": 0
  },
  "alertas": [
    {
      "codigo": "texto",
      "mensagem": "texto"
    }
  ],
  "taxaFalha": 0,
  "erroMaisFrequente": {
    "codigo": "texto",
    "mensagem": "texto",
    "quantidade": 0
  },
  "criadoEm": "texto",
  "estimativa": {
    "categoria": "MARKETING",
    "quantidade": 0,
    "ignoradosOptout": 0,
    "centavos": 0,
    "porPais": [
      {
        "ddi": "texto",
        "rotulo": "texto",
        "quantidade": 0,
        "precoUsd": "texto",
        "centavosPorMensagem": 0,
        "centavos": 0
      }
    ],
    "cambio": "texto",
    "tabelaVersao": 0,
    "semPreco": [
      "texto"
    ],
    "estimado": true
  },
  "contatos": {
    "recebidos": 0,
    "aceitos": 0,
    "rejeitados": 0,
    "duplicados": 0,
    "rejeitadasAmostra": [
      {
        "linha": 0,
        "telefone": "texto",
        "motivo": "texto"
      }
    ]
  }
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
get /v1/campanhas/{id}

Status e totais de uma campanha

Totais por status do envio (pendente, enviado, entregue, lido, respondido, falhou, ignorado por opt-out), taxa de falha e o erro mais frequente.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da campanha.

Resposta 200 · OK

A campanha.

CampoTipoObrig.Descrição
idstringsim
nomestringsim
statusenumsim

Valores: RASCUNHO, AGENDADA, EM_ANDAMENTO, PAUSADA, PAUSADA_AUTOMATICAMENTE, CONCLUIDA, CANCELADA.

numeroobjectsim
numero.idstringsim
numero.apelidostringsim
templateobjectsim
template.idstringsim
template.nomestringsim
listaobject | nullsim
lista.idstringsim
lista.nomestringsim
agendadaParastring | nullsim
iniciadaEmstring | nullsim
concluidaEmstring | nullsim
pausaAutomaticabooleansim
motivoPausastring | nullsim
custoEstimadoCentavosintegersim
totalintegersim

Contatos da campanha.

totaisobjectsim
totais.pendenteintegersim
totais.enviadointegersim
totais.entregueintegersim
totais.lidointegersim
totais.respondidointegersim
totais.falhouintegersim
totais.ignoradoOptoutintegersim
alertasobject[]sim

Informativos; nunca bloqueiam.

alertas[].codigostringsim
alertas[].mensagemstringsim
taxaFalhanumbersim

Falhas ÷ (enviados + falhas), em %.

erroMaisFrequenteobject | nullsim
erroMaisFrequente.codigostringsim
erroMaisFrequente.mensagemstringsim
erroMaisFrequente.quantidadeintegersim
criadoEmstringsim
Exemplo de resposta
{
  "id": "clx0campanha00000000000001",
  "nome": "Promo de outubro",
  "status": "EM_ANDAMENTO",
  "numero": {
    "id": "clx0numero000000000000001",
    "apelido": "Oficial"
  },
  "template": {
    "id": "clx0template0000000000001",
    "nome": "promo_outubro"
  },
  "lista": {
    "id": "clx0lista0000000000000001",
    "nome": "Promo de outubro (API)"
  },
  "agendadaPara": null,
  "iniciadaEm": "2026-10-14T13:00:00.000Z",
  "concluidaEm": null,
  "pausaAutomatica": false,
  "motivoPausa": null,
  "custoEstimadoCentavos": 10200,
  "total": 3000,
  "totais": {
    "pendente": 2400,
    "enviado": 600,
    "entregue": 0,
    "lido": 0,
    "respondido": 0,
    "falhou": 0,
    "ignoradoOptout": 0
  },
  "alertas": [],
  "taxaFalha": 0,
  "erroMaisFrequente": null,
  "criadoEm": "2026-10-14T13:00:00.000Z"
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/campanhas/{id}/iniciar

Inicia a campanha

Dispara uma campanha em rascunho ou agendada. Estado incompatível responde 409.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da campanha.

Resposta 200 · OK

A campanha atualizada.

CampoTipoObrig.Descrição
idstringsim
nomestringsim
statusenumsim

Valores: RASCUNHO, AGENDADA, EM_ANDAMENTO, PAUSADA, PAUSADA_AUTOMATICAMENTE, CONCLUIDA, CANCELADA.

numeroobjectsim
numero.idstringsim
numero.apelidostringsim
templateobjectsim
template.idstringsim
template.nomestringsim
listaobject | nullsim
lista.idstringsim
lista.nomestringsim
agendadaParastring | nullsim
iniciadaEmstring | nullsim
concluidaEmstring | nullsim
pausaAutomaticabooleansim
motivoPausastring | nullsim
custoEstimadoCentavosintegersim
totalintegersim

Contatos da campanha.

totaisobjectsim
totais.pendenteintegersim
totais.enviadointegersim
totais.entregueintegersim
totais.lidointegersim
totais.respondidointegersim
totais.falhouintegersim
totais.ignoradoOptoutintegersim
alertasobject[]sim

Informativos; nunca bloqueiam.

alertas[].codigostringsim
alertas[].mensagemstringsim
taxaFalhanumbersim

Falhas ÷ (enviados + falhas), em %.

erroMaisFrequenteobject | nullsim
erroMaisFrequente.codigostringsim
erroMaisFrequente.mensagemstringsim
erroMaisFrequente.quantidadeintegersim
criadoEmstringsim
Exemplo de resposta
{
  "id": "texto",
  "nome": "texto",
  "status": "RASCUNHO",
  "numero": {
    "id": "texto",
    "apelido": "texto"
  },
  "template": {
    "id": "texto",
    "nome": "texto"
  },
  "lista": {
    "id": "texto",
    "nome": "texto"
  },
  "agendadaPara": "texto",
  "iniciadaEm": "texto",
  "concluidaEm": "texto",
  "pausaAutomatica": false,
  "motivoPausa": "texto",
  "custoEstimadoCentavos": 0,
  "total": 0,
  "totais": {
    "pendente": 0,
    "enviado": 0,
    "entregue": 0,
    "lido": 0,
    "respondido": 0,
    "falhou": 0,
    "ignoradoOptout": 0
  },
  "alertas": [
    {
      "codigo": "texto",
      "mensagem": "texto"
    }
  ],
  "taxaFalha": 0,
  "erroMaisFrequente": {
    "codigo": "texto",
    "mensagem": "texto",
    "quantidade": 0
  },
  "criadoEm": "texto"
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/campanhas/{id}/pausar

Pausa a campanha

Para o envio: nenhum contato novo sai; os pendentes ficam guardados. Estado incompatível responde 409.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da campanha.

Resposta 200 · OK

A campanha atualizada.

CampoTipoObrig.Descrição
idstringsim
nomestringsim
statusenumsim

Valores: RASCUNHO, AGENDADA, EM_ANDAMENTO, PAUSADA, PAUSADA_AUTOMATICAMENTE, CONCLUIDA, CANCELADA.

numeroobjectsim
numero.idstringsim
numero.apelidostringsim
templateobjectsim
template.idstringsim
template.nomestringsim
listaobject | nullsim
lista.idstringsim
lista.nomestringsim
agendadaParastring | nullsim
iniciadaEmstring | nullsim
concluidaEmstring | nullsim
pausaAutomaticabooleansim
motivoPausastring | nullsim
custoEstimadoCentavosintegersim
totalintegersim

Contatos da campanha.

totaisobjectsim
totais.pendenteintegersim
totais.enviadointegersim
totais.entregueintegersim
totais.lidointegersim
totais.respondidointegersim
totais.falhouintegersim
totais.ignoradoOptoutintegersim
alertasobject[]sim

Informativos; nunca bloqueiam.

alertas[].codigostringsim
alertas[].mensagemstringsim
taxaFalhanumbersim

Falhas ÷ (enviados + falhas), em %.

erroMaisFrequenteobject | nullsim
erroMaisFrequente.codigostringsim
erroMaisFrequente.mensagemstringsim
erroMaisFrequente.quantidadeintegersim
criadoEmstringsim
Exemplo de resposta
{
  "id": "texto",
  "nome": "texto",
  "status": "RASCUNHO",
  "numero": {
    "id": "texto",
    "apelido": "texto"
  },
  "template": {
    "id": "texto",
    "nome": "texto"
  },
  "lista": {
    "id": "texto",
    "nome": "texto"
  },
  "agendadaPara": "texto",
  "iniciadaEm": "texto",
  "concluidaEm": "texto",
  "pausaAutomatica": false,
  "motivoPausa": "texto",
  "custoEstimadoCentavos": 0,
  "total": 0,
  "totais": {
    "pendente": 0,
    "enviado": 0,
    "entregue": 0,
    "lido": 0,
    "respondido": 0,
    "falhou": 0,
    "ignoradoOptout": 0
  },
  "alertas": [
    {
      "codigo": "texto",
      "mensagem": "texto"
    }
  ],
  "taxaFalha": 0,
  "erroMaisFrequente": {
    "codigo": "texto",
    "mensagem": "texto",
    "quantidade": 0
  },
  "criadoEm": "texto"
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/campanhas/{id}/retomar

Retoma a campanha

Continua dos pendentes (quem já foi enviado nunca é reenviado). Estado incompatível responde 409.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da campanha.

Resposta 200 · OK

A campanha atualizada.

CampoTipoObrig.Descrição
idstringsim
nomestringsim
statusenumsim

Valores: RASCUNHO, AGENDADA, EM_ANDAMENTO, PAUSADA, PAUSADA_AUTOMATICAMENTE, CONCLUIDA, CANCELADA.

numeroobjectsim
numero.idstringsim
numero.apelidostringsim
templateobjectsim
template.idstringsim
template.nomestringsim
listaobject | nullsim
lista.idstringsim
lista.nomestringsim
agendadaParastring | nullsim
iniciadaEmstring | nullsim
concluidaEmstring | nullsim
pausaAutomaticabooleansim
motivoPausastring | nullsim
custoEstimadoCentavosintegersim
totalintegersim

Contatos da campanha.

totaisobjectsim
totais.pendenteintegersim
totais.enviadointegersim
totais.entregueintegersim
totais.lidointegersim
totais.respondidointegersim
totais.falhouintegersim
totais.ignoradoOptoutintegersim
alertasobject[]sim

Informativos; nunca bloqueiam.

alertas[].codigostringsim
alertas[].mensagemstringsim
taxaFalhanumbersim

Falhas ÷ (enviados + falhas), em %.

erroMaisFrequenteobject | nullsim
erroMaisFrequente.codigostringsim
erroMaisFrequente.mensagemstringsim
erroMaisFrequente.quantidadeintegersim
criadoEmstringsim
Exemplo de resposta
{
  "id": "texto",
  "nome": "texto",
  "status": "RASCUNHO",
  "numero": {
    "id": "texto",
    "apelido": "texto"
  },
  "template": {
    "id": "texto",
    "nome": "texto"
  },
  "lista": {
    "id": "texto",
    "nome": "texto"
  },
  "agendadaPara": "texto",
  "iniciadaEm": "texto",
  "concluidaEm": "texto",
  "pausaAutomatica": false,
  "motivoPausa": "texto",
  "custoEstimadoCentavos": 0,
  "total": 0,
  "totais": {
    "pendente": 0,
    "enviado": 0,
    "entregue": 0,
    "lido": 0,
    "respondido": 0,
    "falhou": 0,
    "ignoradoOptout": 0
  },
  "alertas": [
    {
      "codigo": "texto",
      "mensagem": "texto"
    }
  ],
  "taxaFalha": 0,
  "erroMaisFrequente": {
    "codigo": "texto",
    "mensagem": "texto",
    "quantidade": 0
  },
  "criadoEm": "texto"
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/campanhas/{id}/cancelar

Cancela a campanha

Encerra a campanha; não pode ser retomada. Estado incompatível responde 409.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador da campanha.

Resposta 200 · OK

A campanha atualizada.

CampoTipoObrig.Descrição
idstringsim
nomestringsim
statusenumsim

Valores: RASCUNHO, AGENDADA, EM_ANDAMENTO, PAUSADA, PAUSADA_AUTOMATICAMENTE, CONCLUIDA, CANCELADA.

numeroobjectsim
numero.idstringsim
numero.apelidostringsim
templateobjectsim
template.idstringsim
template.nomestringsim
listaobject | nullsim
lista.idstringsim
lista.nomestringsim
agendadaParastring | nullsim
iniciadaEmstring | nullsim
concluidaEmstring | nullsim
pausaAutomaticabooleansim
motivoPausastring | nullsim
custoEstimadoCentavosintegersim
totalintegersim

Contatos da campanha.

totaisobjectsim
totais.pendenteintegersim
totais.enviadointegersim
totais.entregueintegersim
totais.lidointegersim
totais.respondidointegersim
totais.falhouintegersim
totais.ignoradoOptoutintegersim
alertasobject[]sim

Informativos; nunca bloqueiam.

alertas[].codigostringsim
alertas[].mensagemstringsim
taxaFalhanumbersim

Falhas ÷ (enviados + falhas), em %.

erroMaisFrequenteobject | nullsim
erroMaisFrequente.codigostringsim
erroMaisFrequente.mensagemstringsim
erroMaisFrequente.quantidadeintegersim
criadoEmstringsim
Exemplo de resposta
{
  "id": "texto",
  "nome": "texto",
  "status": "RASCUNHO",
  "numero": {
    "id": "texto",
    "apelido": "texto"
  },
  "template": {
    "id": "texto",
    "nome": "texto"
  },
  "lista": {
    "id": "texto",
    "nome": "texto"
  },
  "agendadaPara": "texto",
  "iniciadaEm": "texto",
  "concluidaEm": "texto",
  "pausaAutomatica": false,
  "motivoPausa": "texto",
  "custoEstimadoCentavos": 0,
  "total": 0,
  "totais": {
    "pendente": 0,
    "enviado": 0,
    "entregue": 0,
    "lido": 0,
    "respondido": 0,
    "falhou": 0,
    "ignoradoOptout": 0
  },
  "alertas": [
    {
      "codigo": "texto",
      "mensagem": "texto"
    }
  ],
  "taxaFalha": 0,
  "erroMaisFrequente": {
    "codigo": "texto",
    "mensagem": "texto",
    "quantidade": 0
  },
  "criadoEm": "texto"
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.

Leads

Leads captados pelo número oficial e seu repasse a números não oficiais.

Exemplo: Listar leads novos e fazer o repasse

curl "https://api.zaphub.wssengenharia.com.br/v1/leads?status=NOVO&limite=30" -H "Authorization: Bearer $ZAPHUB_KEY"

# Repasse a um número não oficial (sem o corpo, usa o rodízio da regra do número oficial)
curl -X POST https://api.zaphub.wssengenharia.com.br/v1/leads/clx0lead000000000000000001/repassar \
  -H "Authorization: Bearer $ZAPHUB_KEY" -H "Content-Type: application/json" \
  -d '{"numeroId":"clx0numero000000000000001"}'

curl -X POST https://api.zaphub.wssengenharia.com.br/v1/leads/clx0lead000000000000000001/descartar -H "Authorization: Bearer $ZAPHUB_KEY"
import { zaphub } from "./zaphub.js";

const { itens } = await zaphub("GET", "/v1/leads?status=NOVO");
for (const lead of itens) {
  const r = await zaphub("POST", "/v1/leads/" + lead.id + "/repassar", { numeroId: "clx0numero000000000000001" });
  console.log(r.lead.status, r.contatoOptout);
}
<?php
require 'zaphub.php';

[, $lista] = zaphub('GET', '/v1/leads?status=NOVO');
foreach ($lista['itens'] as $lead) {
    [, $r] = zaphub('POST', '/v1/leads/' . $lead['id'] . '/repassar', ['numeroId' => 'clx0numero000000000000001']);
    echo $r['lead']['status'] . PHP_EOL;
}
get /v1/leads

Lista os leads

Filtros por status, origem, número, campanha, anúncio e período. Paginação por cursor (proximoCursor → cursor).

Parâmetros de consulta (query string)

ParâmetroTipoObrig.Descrição
statusstringnão

Valores: NOVO, REPASSADO, EM_CONTATO, RESPONDEU, SEM_RESPOSTA, DESCARTADO.

origemstringnão

Valores: CAMPANHA, ANUNCIO, ORGANICO.

numeroOficialIdstringnão
numeroAtribuidoIdstringnão
campanhaIdstringnão
anuncioIdstringnão
destringnão
atestringnão
limiteintegernão
cursorstringnão

Resposta 200 · OK

Página de leads.

CampoTipoObrig.Descrição
itensobject[]sim
itens[].idstringsim
itens[].contatoTelefonestringsim
itens[].contatoNomestring | nullsim
itens[].origemenumsim

Valores: CAMPANHA, ANUNCIO, ORGANICO.

itens[].campanhaobject | nullsim
itens[].campanha.idstringsim
itens[].campanha.nomestringsim
itens[].anuncioobject | nullsim
itens[].anuncio.idstringsim
itens[].anuncio.titulostring | nullsim
itens[].anuncio.urlstring | nullsim
itens[].primeiraMensagemstring | nullsim
itens[].statusenumsim

Valores: NOVO, REPASSADO, EM_CONTATO, RESPONDEU, SEM_RESPOSTA, DESCARTADO.

itens[].numeroOficialobjectsim
itens[].numeroOficial.idstringsim
itens[].numeroOficial.apelidostringsim
itens[].numeroOficial.telefonestring | nullsim
itens[].numeroAtribuidoobject | nullsim
itens[].numeroAtribuido.idstringsim
itens[].numeroAtribuido.apelidostringsim
itens[].numeroAtribuido.telefonestring | nullsim
itens[].conversaIdstring | nullsim
itens[].contatoOptoutbooleansim
itens[].repassadoEmstring | nullsim
itens[].respondeuEmstring | nullsim
itens[].criadoEmstringsim
itens[].economiaobjectsim
itens[].economia.qtdintegersim
itens[].economia.marketingCentavosintegersim
itens[].economia.utilityCentavosintegersim
proximoCursorstring | nullsim
Exemplo de resposta
{
  "itens": [
    {
      "id": "texto",
      "contatoTelefone": "texto",
      "contatoNome": "texto",
      "origem": "CAMPANHA",
      "campanha": {
        "id": "texto",
        "nome": "texto"
      },
      "anuncio": {
        "id": "texto",
        "titulo": "texto",
        "url": "texto"
      },
      "primeiraMensagem": "texto",
      "status": "NOVO",
      "numeroOficial": {
        "id": "texto",
        "apelido": "texto",
        "telefone": "texto"
      },
      "numeroAtribuido": {
        "id": "texto",
        "apelido": "texto",
        "telefone": "texto"
      },
      "conversaId": "texto",
      "contatoOptout": false,
      "repassadoEm": "texto",
      "respondeuEm": "texto",
      "criadoEm": "texto",
      "economia": {
        "qtd": 0,
        "marketingCentavos": 0,
        "utilityCentavos": 0
      }
    }
  ],
  "proximoCursor": "texto"
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
get /v1/leads/{id}

Detalhe de um lead

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do lead.

Resposta 200 · OK

O lead.

CampoTipoObrig.Descrição
idstringsim
contatoTelefonestringsim
contatoNomestring | nullsim
origemenumsim

Valores: CAMPANHA, ANUNCIO, ORGANICO.

campanhaobject | nullsim
campanha.idstringsim
campanha.nomestringsim
anuncioobject | nullsim
anuncio.idstringsim
anuncio.titulostring | nullsim
anuncio.urlstring | nullsim
primeiraMensagemstring | nullsim
statusenumsim

Valores: NOVO, REPASSADO, EM_CONTATO, RESPONDEU, SEM_RESPOSTA, DESCARTADO.

numeroOficialobjectsim
numeroOficial.idstringsim
numeroOficial.apelidostringsim
numeroOficial.telefonestring | nullsim
numeroAtribuidoobject | nullsim
numeroAtribuido.idstringsim
numeroAtribuido.apelidostringsim
numeroAtribuido.telefonestring | nullsim
conversaIdstring | nullsim
contatoOptoutbooleansim
repassadoEmstring | nullsim
respondeuEmstring | nullsim
criadoEmstringsim
economiaobjectsim
economia.qtdintegersim
economia.marketingCentavosintegersim
economia.utilityCentavosintegersim
Exemplo de resposta
{
  "id": "texto",
  "contatoTelefone": "texto",
  "contatoNome": "texto",
  "origem": "CAMPANHA",
  "campanha": {
    "id": "texto",
    "nome": "texto"
  },
  "anuncio": {
    "id": "texto",
    "titulo": "texto",
    "url": "texto"
  },
  "primeiraMensagem": "texto",
  "status": "NOVO",
  "numeroOficial": {
    "id": "texto",
    "apelido": "texto",
    "telefone": "texto"
  },
  "numeroAtribuido": {
    "id": "texto",
    "apelido": "texto",
    "telefone": "texto"
  },
  "conversaId": "texto",
  "contatoOptout": false,
  "repassadoEm": "texto",
  "respondeuEm": "texto",
  "criadoEm": "texto",
  "economia": {
    "qtd": 0,
    "marketingCentavos": 0,
    "utilityCentavos": 0
  }
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/leads/{id}/repassar

Repassa o lead a um número não oficial

Sem numeroId usa o rodízio da regra do número oficial; com numeroId repassa àquele número não oficial conectado. O ZapHub envia a mensagem de primeiro contato configurada. Contato em opt-out não bloqueia: vem sinalizado em contatoOptout. Já repassado: 409; descartado: 422.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do lead.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
numeroIdstringnão
Exemplo de corpo
{
  "numeroId": "clx0numero000000000000001"
}

Resposta 200 · OK

O lead repassado.

CampoTipoObrig.Descrição
leadobjectsim
lead.idstringsim
lead.contatoTelefonestringsim
lead.contatoNomestring | nullsim
lead.origemenumsim

Valores: CAMPANHA, ANUNCIO, ORGANICO.

lead.campanhaobject | nullsim
lead.campanha.idstringsim
lead.campanha.nomestringsim
lead.anuncioobject | nullsim
lead.anuncio.idstringsim
lead.anuncio.titulostring | nullsim
lead.anuncio.urlstring | nullsim
lead.primeiraMensagemstring | nullsim
lead.statusenumsim

Valores: NOVO, REPASSADO, EM_CONTATO, RESPONDEU, SEM_RESPOSTA, DESCARTADO.

lead.numeroOficialobjectsim
lead.numeroOficial.idstringsim
lead.numeroOficial.apelidostringsim
lead.numeroOficial.telefonestring | nullsim
lead.numeroAtribuidoobject | nullsim
lead.numeroAtribuido.idstringsim
lead.numeroAtribuido.apelidostringsim
lead.numeroAtribuido.telefonestring | nullsim
lead.conversaIdstring | nullsim
lead.contatoOptoutbooleansim
lead.repassadoEmstring | nullsim
lead.respondeuEmstring | nullsim
lead.criadoEmstringsim
lead.economiaobjectsim
lead.economia.qtdintegersim
lead.economia.marketingCentavosintegersim
lead.economia.utilityCentavosintegersim
contatoOptoutbooleansim
Exemplo de resposta
{
  "lead": {
    "id": "texto",
    "contatoTelefone": "texto",
    "contatoNome": "texto",
    "origem": "CAMPANHA",
    "campanha": {
      "id": "texto",
      "nome": "texto"
    },
    "anuncio": {
      "id": "texto",
      "titulo": "texto",
      "url": "texto"
    },
    "primeiraMensagem": "texto",
    "status": "NOVO",
    "numeroOficial": {
      "id": "texto",
      "apelido": "texto",
      "telefone": "texto"
    },
    "numeroAtribuido": {
      "id": "texto",
      "apelido": "texto",
      "telefone": "texto"
    },
    "conversaId": "texto",
    "contatoOptout": false,
    "repassadoEm": "texto",
    "respondeuEm": "texto",
    "criadoEm": "texto",
    "economia": {
      "qtd": 0,
      "marketingCentavos": 0,
      "utilityCentavos": 0
    }
  },
  "contatoOptout": false
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 409 Conflito : Conflito com o estado atual do recurso.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/leads/{id}/descartar

Descarta o lead

Idempotente. Cancela um repasse automático pendente.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do lead.

Resposta 200 · OK

O lead descartado.

CampoTipoObrig.Descrição
idstringsim
contatoTelefonestringsim
contatoNomestring | nullsim
origemenumsim

Valores: CAMPANHA, ANUNCIO, ORGANICO.

campanhaobject | nullsim
campanha.idstringsim
campanha.nomestringsim
anuncioobject | nullsim
anuncio.idstringsim
anuncio.titulostring | nullsim
anuncio.urlstring | nullsim
primeiraMensagemstring | nullsim
statusenumsim

Valores: NOVO, REPASSADO, EM_CONTATO, RESPONDEU, SEM_RESPOSTA, DESCARTADO.

numeroOficialobjectsim
numeroOficial.idstringsim
numeroOficial.apelidostringsim
numeroOficial.telefonestring | nullsim
numeroAtribuidoobject | nullsim
numeroAtribuido.idstringsim
numeroAtribuido.apelidostringsim
numeroAtribuido.telefonestring | nullsim
conversaIdstring | nullsim
contatoOptoutbooleansim
repassadoEmstring | nullsim
respondeuEmstring | nullsim
criadoEmstringsim
economiaobjectsim
economia.qtdintegersim
economia.marketingCentavosintegersim
economia.utilityCentavosintegersim
Exemplo de resposta
{
  "id": "texto",
  "contatoTelefone": "texto",
  "contatoNome": "texto",
  "origem": "CAMPANHA",
  "campanha": {
    "id": "texto",
    "nome": "texto"
  },
  "anuncio": {
    "id": "texto",
    "titulo": "texto",
    "url": "texto"
  },
  "primeiraMensagem": "texto",
  "status": "NOVO",
  "numeroOficial": {
    "id": "texto",
    "apelido": "texto",
    "telefone": "texto"
  },
  "numeroAtribuido": {
    "id": "texto",
    "apelido": "texto",
    "telefone": "texto"
  },
  "conversaId": "texto",
  "contatoOptout": false,
  "repassadoEm": "texto",
  "respondeuEm": "texto",
  "criadoEm": "texto",
  "economia": {
    "qtd": 0,
    "marketingCentavos": 0,
    "utilityCentavos": 0
  }
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.

Economia

Quanto o cliente economiza em Marketing e Utility usando números não oficiais.

Exemplo: Economia do mês no seu sistema

curl "https://api.zaphub.wssengenharia.com.br/v1/economia?inicio=2026-10-01&fim=2026-10-31" -H "Authorization: Bearer $ZAPHUB_KEY"

# {"totais":{"qtd":1280,"marketingCentavos":43520,"utilityCentavos":8704},
#  "porTipoEnvio":[...], "tabelaPrecos":{"versao":3,"vigenteDe":"2026-07-01T00:00:00.000Z", ...}}
import { zaphub } from "./zaphub.js";

const e = await zaphub("GET", "/v1/economia?inicio=2026-10-01&fim=2026-10-31");
const reais = (centavos) => (centavos / 100).toLocaleString("pt-BR", { style: "currency", currency: "BRL" });
console.log("Economia em Marketing:", reais(e.totais.marketingCentavos));
console.log("Economia em Utility:", reais(e.totais.utilityCentavos));
<?php
require 'zaphub.php';

[, $e] = zaphub('GET', '/v1/economia?inicio=2026-10-01&fim=2026-10-31');
printf("Marketing: R$ %.2f | Utility: R$ %.2f\n", $e['totais']['marketingCentavos'] / 100, $e['totais']['utilityCentavos'] / 100);
get /v1/economia

Economia do período

Os mesmos totais do dashboard, em centavos de BRL: marketingCentavos e utilityCentavos (o que as mensagens enviadas por números não oficiais custariam na API Oficial), o detalhamento por tipo de envio, o gasto estimado no oficial e a data da tabela de preços da Meta usada (tabelaPrecos.vigenteDe). Padrão: do primeiro dia do mês corrente até agora (America/Sao_Paulo).

Parâmetros de consulta (query string)

ParâmetroTipoObrig.Descrição
iniciostringnão

Padrão: primeiro dia do mês corrente (America/Sao_Paulo).

fimstringnão

Padrão: agora.

numeroIdstringnão

Resposta 200 · OK

Resumo da economia.

CampoTipoObrig.Descrição
periodoobjectsim

Dias (America/Sao_Paulo), AAAA-MM-DD.

periodo.iniciostringsim
periodo.fimstringsim
numeroIdstring | nullsim
totaisobjectsim

Os mesmos totais do dashboard, em centavos de BRL.

totais.marketingCentavosintegersim
totais.utilityCentavosintegersim
totais.qtdintegersim
porTipoEnvioobject[]sim
porTipoEnvio[].marketingCentavosintegersim
porTipoEnvio[].utilityCentavosintegersim
porTipoEnvio[].tipoEnvioenumsim

Valores: PRIMEIRO_CONTATO, PRIMEIRA_RESPOSTA, CONVERSA, RETOMADA, CONTATO_LEAD, AUTO_RESPOSTA, CAMPANHA, GRUPO.

porTipoEnvio[].qtdintegersim
gastoOficialobjectsim
gastoOficial.centavosintegersim
gastoOficial.qtdintegersim
gastoOficial.porCategoriaobject[]sim
gastoOficial.porCategoria[].categoriaenumsim

Valores: MARKETING, UTILITY, AUTHENTICATION.

gastoOficial.porCategoria[].qtdintegersim
gastoOficial.porCategoria[].centavosintegersim
gastoOficial.estimadoenumsim

Valores: true.

porOrigemLeadobjectsim
porOrigemLead.campanhasobject[]sim
porOrigemLead.campanhas[].marketingCentavosintegersim
porOrigemLead.campanhas[].utilityCentavosintegersim
porOrigemLead.campanhas[].campanhaIdstringsim
porOrigemLead.campanhas[].nomestringsim
porOrigemLead.campanhas[].qtdintegersim
porOrigemLead.anunciosobject[]sim
porOrigemLead.anuncios[].marketingCentavosintegersim
porOrigemLead.anuncios[].utilityCentavosintegersim
porOrigemLead.anuncios[].anuncioIdstringsim
porOrigemLead.anuncios[].titulostring | nullsim
porOrigemLead.anuncios[].qtdintegersim
tabelaPrecosobject | nullsim
tabelaPrecos.versaointegersim
tabelaPrecos.vigenteDestringsim

Data da tabela de preços da Meta usada no cálculo.

tabelaPrecos.desatualizadabooleansim
tabelaPrecos.cambioobjectsim
tabelaPrecos.cambio.usdBrlstringsim
tabelaPrecos.cambio.vigenteDestringsim
tabelaPrecos.cambio.fontestringsim
Exemplo de resposta
{
  "periodo": {
    "inicio": "texto",
    "fim": "texto"
  },
  "numeroId": "texto",
  "totais": {
    "marketingCentavos": 0,
    "utilityCentavos": 0,
    "qtd": 0
  },
  "porTipoEnvio": [
    {
      "marketingCentavos": 0,
      "utilityCentavos": 0,
      "tipoEnvio": "PRIMEIRO_CONTATO",
      "qtd": 0
    }
  ],
  "gastoOficial": {
    "centavos": 0,
    "qtd": 0,
    "porCategoria": [
      {
        "categoria": "MARKETING",
        "qtd": 0,
        "centavos": 0
      }
    ],
    "estimado": true
  },
  "porOrigemLead": {
    "campanhas": [
      {
        "marketingCentavos": 0,
        "utilityCentavos": 0,
        "campanhaId": "texto",
        "nome": "texto",
        "qtd": 0
      }
    ],
    "anuncios": [
      {
        "marketingCentavos": 0,
        "utilityCentavos": 0,
        "anuncioId": "texto",
        "titulo": "texto",
        "qtd": 0
      }
    ]
  },
  "tabelaPrecos": {
    "versao": 0,
    "vigenteDe": "texto",
    "desatualizada": false,
    "cambio": {
      "usdBrl": "texto",
      "vigenteDe": "texto",
      "fonte": "texto"
    }
  }
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.

Destinos de eventos

Onde o ZapHub entrega os eventos: webhook HTTPS assinado, RabbitMQ (exchange ou uma fila por evento), NATS, Kafka ou Amazon SQS do próprio cliente.

Exemplo: Webhook e RabbitMQ

# Webhook HTTPS
curl -X POST https://api.zaphub.wssengenharia.com.br/v1/destinos-eventos \
  -H "Authorization: Bearer $ZAPHUB_KEY" -H "Content-Type: application/json" \
  -d '{"tipo":"WEBHOOK","nome":"Meu CRM","url":"https://crm.exemplo.com.br/hooks/zaphub","eventos":["mensagem.recebida","lead.criado"]}'

# RabbitMQ do cliente (amqps por padrão)
curl -X POST https://api.zaphub.wssengenharia.com.br/v1/destinos-eventos \
  -H "Authorization: Bearer $ZAPHUB_KEY" -H "Content-Type: application/json" \
  -d '{"tipo":"RABBITMQ","nome":"Broker","host":"mq.exemplo.com.br","port":5671,"vhost":"/","usuario":"zaphub","senha":"********",
       "exchange":"zaphub","prefixo":"zaphub","eventos":["mensagem.recebida","mensagem.status"]}'
import { zaphub } from "./zaphub.js";

await zaphub("POST", "/v1/destinos-eventos", {
  tipo: "WEBHOOK",
  nome: "Meu CRM",
  url: "https://crm.exemplo.com.br/hooks/zaphub",
  eventos: ["mensagem.recebida", "lead.criado"],
});

const rabbit = await zaphub("POST", "/v1/destinos-eventos", {
  tipo: "RABBITMQ",
  nome: "Broker",
  host: "mq.exemplo.com.br",
  port: 5671,
  vhost: "/",
  usuario: "zaphub",
  senha: process.env.AMQP_SENHA,
  exchange: "zaphub",
  prefixo: "zaphub",
  eventos: ["mensagem.recebida", "mensagem.status"],
});
<?php
require 'zaphub.php';

zaphub('POST', '/v1/destinos-eventos', [
    'tipo' => 'WEBHOOK',
    'nome' => 'Meu CRM',
    'url' => 'https://crm.exemplo.com.br/hooks/zaphub',
    'eventos' => ['mensagem.recebida', 'lead.criado'],
]);

[, $rabbit] = zaphub('POST', '/v1/destinos-eventos', [
    'tipo' => 'RABBITMQ',
    'nome' => 'Broker',
    'host' => 'mq.exemplo.com.br',
    'port' => 5671,
    'vhost' => '/',
    'usuario' => 'zaphub',
    'senha' => getenv('AMQP_SENHA'),
    'exchange' => 'zaphub',
    'prefixo' => 'zaphub',
    'eventos' => ['mensagem.recebida', 'mensagem.status'],
]);

Exemplo: Testar a conexão, ver o log de entregas e reenviar

curl -X POST https://api.zaphub.wssengenharia.com.br/v1/destinos-eventos/clx0destino0000000000001/testar -H "Authorization: Bearer $ZAPHUB_KEY"
curl "https://api.zaphub.wssengenharia.com.br/v1/destinos-eventos/clx0destino0000000000001/entregas?status=FALHOU" -H "Authorization: Bearer $ZAPHUB_KEY"
curl -X POST https://api.zaphub.wssengenharia.com.br/v1/destinos-eventos/entregas/ENTREGA_ID/reenviar -H "Authorization: Bearer $ZAPHUB_KEY"
import { zaphub } from "./zaphub.js";

const id = "clx0destino0000000000001";
const teste = await zaphub("POST", "/v1/destinos-eventos/" + id + "/testar");
if (!teste.sucesso) console.error(teste.erro);

const { itens } = await zaphub("GET", "/v1/destinos-eventos/" + id + "/entregas?status=FALHOU");
for (const e of itens) await zaphub("POST", "/v1/destinos-eventos/entregas/" + e.id + "/reenviar");
<?php
require 'zaphub.php';

$id = 'clx0destino0000000000001';
[, $teste] = zaphub('POST', "/v1/destinos-eventos/$id/testar");
if (!$teste['sucesso']) error_log($teste['erro']);

[, $lista] = zaphub('GET', "/v1/destinos-eventos/$id/entregas?status=FALHOU");
foreach ($lista['itens'] as $e) zaphub('POST', '/v1/destinos-eventos/entregas/' . $e['id'] . '/reenviar');
get /v1/destinos-eventos

Lista os destinos

Até 5 por conta. Segredos saem sempre mascarados. Chave restrita a números não gerencia destinos (403): é configuração da conta inteira.

Resposta 200 · OK

Destinos.

CampoTipoObrig.Descrição
destinosobject[]sim
destinos[].idstringsim
destinos[].tipoenumsim

Valores: WEBHOOK, RABBITMQ, NATS, KAFKA, SQS.

destinos[].nomestringsim
destinos[].ativobooleansim
destinos[].eventosenum[]sim
destinos[].numeroIdsstring[]sim

Vazio = todos os números.

destinos[].configobjectsim
destinos[].config.urlstringsim
destinos[].config.hoststringsim
destinos[].config.portintegersim
destinos[].config.vhoststringsim
destinos[].config.usuariostringsim

Mascarado.

destinos[].config.senhastringsim

Sempre mascarada.

destinos[].config.exchangestringsim
destinos[].config.prefixostringsim
destinos[].config.tlsbooleansim
destinos[].config.permitirSemTlsbooleansim
destinos[].config.modoenumsim

Valores: exchange, filas.

destinos[].config.servidoresstring[]sim
destinos[].config.autenticacaoenumsim

Valores: nenhuma, token, usuario, creds.

destinos[].config.usuariostring | nullsim

Mascarado.

destinos[].config.senhastring | nullsim

Sempre mascarada.

destinos[].config.jetstreambooleansim
destinos[].config.brokersstring[]sim
destinos[].config.saslenumsim

Valores: nenhum, plain, scram-sha-256, scram-sha-512.

destinos[].config.topicostring | nullsim
destinos[].config.prefixoTopicostring | nullsim
destinos[].config.filaUrlstringsim
destinos[].config.regiaostringsim
destinos[].config.fifobooleansim
destinos[].config.accessKeyIdstringsim

Mascarado.

destinos[].config.secretAccessKeystringsim

Sempre mascarado.

destinos[].segredoHmacstringsim

Segredo de assinatura MASCARADO. O completo só aparece na criação/rotação.

destinos[].criadoEmstringsim
destinos[].atualizadoEmstringsim
Exemplo de resposta
{
  "destinos": [
    {
      "id": "texto",
      "tipo": "WEBHOOK",
      "nome": "texto",
      "ativo": false,
      "eventos": [
        "mensagem.recebida"
      ],
      "numeroIds": [
        "texto"
      ],
      "config": {
        "url": "texto"
      },
      "segredoHmac": "texto",
      "criadoEm": "texto",
      "atualizadoEm": "texto"
    }
  ]
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/destinos-eventos

Cria um destino (webhook, RabbitMQ, NATS, Kafka ou SQS)

Todos os tipos usam o mesmo envelope, assinatura HMAC, ordem por número e reenvio (até 6 tentativas em 24h). Hosts internos são recusados no cadastro e a cada entrega, e TLS é exigido por padrão.

- WEBHOOK: URL HTTPS pública; assinatura no header X-ZapHub-Signature.
- RABBITMQ: broker do cliente. modo: "exchange" só publica (routing key <prefixo>.<evento>); modo: "filas" declara uma fila durável por evento (<prefixo>.<evento>) e nunca a apaga.
- NATS: assunto <prefixo>.<contaId>.<numeroId>.<evento> (eventos sem número usam conta); jetstream: true aguarda o ack do stream e deduplica por Nats-Msg-Id.
- KAFKA: topico único ou prefixoTopico (um tópico por evento: <prefixoTopico>-mensagem-recebida); chave = numeroId (ordem por número); o ZapHub não cria tópicos.
- SQS: URL da fila (a região sai dela); em fila .fifo, MessageGroupId = número e MessageDeduplicationId = id do evento. Corpo acima de 256 KB falha sem reenvio.

O segredo HMAC completo vem em segredoHmacRevelado uma única vez; use-o para verificar a assinatura. Chave restrita a números não gerencia destinos (403): é configuração da conta inteira.

Headers

HeaderTipoObrig.Descrição
Idempotency-Keystringnão

Chave de idempotência (até 255 caracteres ASCII; ex.: um UUID). A repetição da mesma requisição em até 24h devolve a resposta original sem executar de novo (header Idempotent-Replayed: true). A mesma chave com corpo diferente retorna 422 idempotencia_corpo_diferente.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
tipoenumsim

Valores: WEBHOOK.

nomestringsim

Tamanho: 1 a 80.

ativobooleannão

Padrão: true.

eventosenum[]sim

No máximo 29 itens.

numeroIdsstring[]não

No máximo 100 itens. Padrão: [].

urlstringsim

Tamanho: 1 a 2048.

uristringnão

Tamanho: 0 a 2048.

hoststringnão

Tamanho: 1 a 253.

portintegernão

Intervalo: 1 a 65535.

vhoststringnão

Tamanho: 0 a 255.

usuariostringnão

Tamanho: 1 a 255.

senhastringnão

Tamanho: 1 a 1024.

tlsbooleannão
exchangestringsim

Tamanho: 1 a 255.

prefixostringnão

Tamanho: 0 a 64. Padrão: "zaphub".

permitirSemTlsbooleannão

Padrão: false.

modoenumnão

Valores: exchange, filas. Padrão: "exchange".

servidoresstring[]sim

No máximo 5 itens.

autenticacaoenumnão

Valores: nenhuma, token, usuario, creds. Padrão: "nenhuma".

jetstreambooleannão

Padrão: false.

brokersstring[]sim

No máximo 10 itens.

saslenumnão

Valores: nenhum, plain, scram-sha-256, scram-sha-512. Padrão: "nenhum".

topicostringnão

Tamanho: 1 a 200.

prefixoTopicostringnão

Tamanho: 1 a 200.

filaUrlstringsim

Tamanho: 0 a 300.

accessKeyIdstringsim
secretAccessKeystringsim

Tamanho: 16 a 256.

Exemplo de corpo
{
  "tipo": "WEBHOOK",
  "nome": "Meu CRM",
  "url": "https://crm.exemplo.com.br/hooks/zaphub",
  "eventos": [
    "mensagem.recebida",
    "mensagem.status",
    "lead.criado"
  ]
}

Resposta 201 · Criado

Destino criado.

CampoTipoObrig.Descrição
idstringsim
tipoenumsim

Valores: WEBHOOK, RABBITMQ, NATS, KAFKA, SQS.

nomestringsim
ativobooleansim
eventosenum[]sim
numeroIdsstring[]sim

Vazio = todos os números.

configobjectsim
config.urlstringsim
config.hoststringsim
config.portintegersim
config.vhoststringsim
config.usuariostringsim

Mascarado.

config.senhastringsim

Sempre mascarada.

config.exchangestringsim
config.prefixostringsim
config.tlsbooleansim
config.permitirSemTlsbooleansim
config.modoenumsim

Valores: exchange, filas.

config.servidoresstring[]sim
config.autenticacaoenumsim

Valores: nenhuma, token, usuario, creds.

config.usuariostring | nullsim

Mascarado.

config.senhastring | nullsim

Sempre mascarada.

config.jetstreambooleansim
config.brokersstring[]sim
config.saslenumsim

Valores: nenhum, plain, scram-sha-256, scram-sha-512.

config.topicostring | nullsim
config.prefixoTopicostring | nullsim
config.filaUrlstringsim
config.regiaostringsim
config.fifobooleansim
config.accessKeyIdstringsim

Mascarado.

config.secretAccessKeystringsim

Sempre mascarado.

segredoHmacstringsim

Segredo de assinatura MASCARADO. O completo só aparece na criação/rotação.

criadoEmstringsim
atualizadoEmstringsim
segredoHmacReveladostringnão

Segredo HMAC completo: exibido UMA vez (criação ou rotação). Guarde-o.

Exemplo de resposta
{
  "id": "texto",
  "tipo": "WEBHOOK",
  "nome": "texto",
  "ativo": false,
  "eventos": [
    "mensagem.recebida"
  ],
  "numeroIds": [
    "texto"
  ],
  "config": {
    "url": "texto"
  },
  "segredoHmac": "texto",
  "criadoEm": "texto",
  "atualizadoEm": "texto",
  "segredoHmacRevelado": "texto"
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 422 Regra de negócio : Regra de negócio ou dado que o ZapHub recusa (o `code` diz qual).
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/destinos-eventos/entregas/{entregaId}/reenviar

Reenvia uma entrega

Reenvio manual de uma entrega (útil depois que as 6 tentativas falharam).

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
entregaIdstringsim

id da entrega, de GET /v1/destinos-eventos/{id}/entregas.

Resposta 200 · OK

A entrega reenfileirada.

CampoTipoObrig.Descrição
idstringsim
eventoIdstringsim
eventostringsim
sequenciaintegersim
statusenumsim

Valores: PENDENTE, ENTREGUE, NAO_ROTEADA, FALHOU.

tentativasintegersim
proximaTentativaEmstring | nullsim
ultimoErrostring | nullsim
entregueEmstring | nullsim
criadoEmstringsim
Exemplo de resposta
{
  "id": "texto",
  "eventoId": "texto",
  "evento": "texto",
  "sequencia": 0,
  "status": "PENDENTE",
  "tentativas": 0,
  "proximaTentativaEm": "texto",
  "ultimoErro": "texto",
  "entregueEm": "texto",
  "criadoEm": "texto"
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
get /v1/destinos-eventos/{id}

Detalhe de um destino

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do destino.

Resposta 200 · OK

O destino.

CampoTipoObrig.Descrição
idstringsim
tipoenumsim

Valores: WEBHOOK, RABBITMQ, NATS, KAFKA, SQS.

nomestringsim
ativobooleansim
eventosenum[]sim
numeroIdsstring[]sim

Vazio = todos os números.

configobjectsim
config.urlstringsim
config.hoststringsim
config.portintegersim
config.vhoststringsim
config.usuariostringsim

Mascarado.

config.senhastringsim

Sempre mascarada.

config.exchangestringsim
config.prefixostringsim
config.tlsbooleansim
config.permitirSemTlsbooleansim
config.modoenumsim

Valores: exchange, filas.

config.servidoresstring[]sim
config.autenticacaoenumsim

Valores: nenhuma, token, usuario, creds.

config.usuariostring | nullsim

Mascarado.

config.senhastring | nullsim

Sempre mascarada.

config.jetstreambooleansim
config.brokersstring[]sim
config.saslenumsim

Valores: nenhum, plain, scram-sha-256, scram-sha-512.

config.topicostring | nullsim
config.prefixoTopicostring | nullsim
config.filaUrlstringsim
config.regiaostringsim
config.fifobooleansim
config.accessKeyIdstringsim

Mascarado.

config.secretAccessKeystringsim

Sempre mascarado.

segredoHmacstringsim

Segredo de assinatura MASCARADO. O completo só aparece na criação/rotação.

criadoEmstringsim
atualizadoEmstringsim
Exemplo de resposta
{
  "id": "texto",
  "tipo": "WEBHOOK",
  "nome": "texto",
  "ativo": false,
  "eventos": [
    "mensagem.recebida"
  ],
  "numeroIds": [
    "texto"
  ],
  "config": {
    "url": "texto"
  },
  "segredoHmac": "texto",
  "criadoEm": "texto",
  "atualizadoEm": "texto"
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
patch /v1/destinos-eventos/{id}

Atualiza um destino

Todos os campos são opcionais; o tipo não muda. rotacionarSegredo: true gera um novo segredo HMAC (devolvido uma vez).

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do destino.

Corpo da requisição (JSON)

CampoTipoObrig.Descrição
nomestringnão

Tamanho: 1 a 80.

ativobooleannão
eventosenum[]não

No máximo 29 itens.

numeroIdsstring[]não

No máximo 100 itens.

urlstringnão

Tamanho: 1 a 2048.

uristringnão

Tamanho: 0 a 2048.

hoststringnão

Tamanho: 1 a 253.

portintegernão

Intervalo: 1 a 65535.

vhoststringnão

Tamanho: 0 a 255.

usuariostringnão

Tamanho: 1 a 255.

senhastringnão

Tamanho: 1 a 8192.

tlsbooleannão
exchangestringnão

Tamanho: 1 a 255.

prefixostringnão

Tamanho: 0 a 64.

permitirSemTlsbooleannão
modoenumnão

Valores: exchange, filas.

servidoresstring[]não

No máximo 5 itens.

autenticacaoenumnão

Valores: nenhuma, token, usuario, creds.

jetstreambooleannão
brokersstring[]não

No máximo 10 itens.

saslenumnão

Valores: nenhum, plain, scram-sha-256, scram-sha-512.

topicostring | nullnão

Tamanho: 1 a 200.

prefixoTopicostring | nullnão

Tamanho: 1 a 200.

filaUrlstringnão

Tamanho: 0 a 300.

accessKeyIdstringnão
secretAccessKeystringnão

Tamanho: 16 a 256.

rotacionarSegredobooleannão
Exemplo de corpo
{
  "ativo": false
}

Resposta 200 · OK

O destino atualizado.

CampoTipoObrig.Descrição
idstringsim
tipoenumsim

Valores: WEBHOOK, RABBITMQ, NATS, KAFKA, SQS.

nomestringsim
ativobooleansim
eventosenum[]sim
numeroIdsstring[]sim

Vazio = todos os números.

configobjectsim
config.urlstringsim
config.hoststringsim
config.portintegersim
config.vhoststringsim
config.usuariostringsim

Mascarado.

config.senhastringsim

Sempre mascarada.

config.exchangestringsim
config.prefixostringsim
config.tlsbooleansim
config.permitirSemTlsbooleansim
config.modoenumsim

Valores: exchange, filas.

config.servidoresstring[]sim
config.autenticacaoenumsim

Valores: nenhuma, token, usuario, creds.

config.usuariostring | nullsim

Mascarado.

config.senhastring | nullsim

Sempre mascarada.

config.jetstreambooleansim
config.brokersstring[]sim
config.saslenumsim

Valores: nenhum, plain, scram-sha-256, scram-sha-512.

config.topicostring | nullsim
config.prefixoTopicostring | nullsim
config.filaUrlstringsim
config.regiaostringsim
config.fifobooleansim
config.accessKeyIdstringsim

Mascarado.

config.secretAccessKeystringsim

Sempre mascarado.

segredoHmacstringsim

Segredo de assinatura MASCARADO. O completo só aparece na criação/rotação.

criadoEmstringsim
atualizadoEmstringsim
segredoHmacReveladostringnão

Segredo HMAC completo: exibido UMA vez (criação ou rotação). Guarde-o.

Exemplo de resposta
{
  "id": "texto",
  "tipo": "WEBHOOK",
  "nome": "texto",
  "ativo": false,
  "eventos": [
    "mensagem.recebida"
  ],
  "numeroIds": [
    "texto"
  ],
  "config": {
    "url": "texto"
  },
  "segredoHmac": "texto",
  "criadoEm": "texto",
  "atualizadoEm": "texto",
  "segredoHmacRevelado": "texto"
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
delete /v1/destinos-eventos/{id}

Remove um destino

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do destino.

Resposta 204 · Sem conteúdo

Removido.

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/destinos-eventos/{id}/testar

Testa a conexão

Publica um evento teste.ping e mostra o resultado, ou o erro exato (autenticação, vhost, TLS, timeout, status HTTP).

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do destino.

Resposta 200 · OK

Resultado do teste.

CampoTipoObrig.Descrição
sucessobooleansim
statusHttpinteger | nullsim
latenciaMsintegersim
errostring | nullsim
avisostring | nullsim
Exemplo de resposta
{
  "sucesso": false,
  "statusHttp": 0,
  "latenciaMs": 0,
  "erro": "texto",
  "aviso": "texto"
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
get /v1/destinos-eventos/{id}/entregas

Log de entregas

Entregas do destino com status (PENDENTE, ENTREGUE, NAO_ROTEADA, FALHOU), tentativas e o último erro.

Parâmetros de caminho

ParâmetroTipoObrig.Descrição
idstringsim

Identificador do destino.

Parâmetros de consulta (query string)

ParâmetroTipoObrig.Descrição
paginaintegernão
limiteintegernão
statusstringnão

Valores: PENDENTE, ENTREGUE, NAO_ROTEADA, FALHOU.

Resposta 200 · OK

Página de entregas.

CampoTipoObrig.Descrição
itensobject[]sim
itens[].idstringsim
itens[].eventoIdstringsim
itens[].eventostringsim
itens[].sequenciaintegersim
itens[].statusenumsim

Valores: PENDENTE, ENTREGUE, NAO_ROTEADA, FALHOU.

itens[].tentativasintegersim
itens[].proximaTentativaEmstring | nullsim
itens[].ultimoErrostring | nullsim
itens[].entregueEmstring | nullsim
itens[].criadoEmstringsim
totalintegersim
paginaintegersim
limiteintegersim
Exemplo de resposta
{
  "itens": [
    {
      "id": "texto",
      "eventoId": "texto",
      "evento": "texto",
      "sequencia": 0,
      "status": "PENDENTE",
      "tentativas": 0,
      "proximaTentativaEm": "texto",
      "ultimoErro": "texto",
      "entregueEm": "texto",
      "criadoEm": "texto"
    }
  ],
  "total": 0,
  "pagina": 0,
  "limite": 0
}

Erros possíveis

  • 400 Dados inválidos : Dados inválidos. `fields` indica o que corrigir.
  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 403 Sem acesso a este número : A chave é restrita a números e este recurso está fora do conjunto dela.
  • 404 Não encontrado : Recurso inexistente ou de outra conta.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.

Eventos ao vivo

Catálogo de eventos e WebSocket (wss://…/v1/eventos/ws) para receber eventos em tempo real, com retomada por desde.

get /v1/eventos/catalogo

Catálogo de eventos

Eventos disponíveis (com os tipos de número em que ocorrem e a versão do formato), tipos de destino aceitos e a URL do WebSocket.

Resposta 200 · OK

Catálogo.

CampoTipoObrig.Descrição
eventosobject[]sim
eventos[].nomeenumsim

Valores: mensagem.recebida, mensagem.enviada, mensagem.status, numero.status, numero.qrcode, contato.optout, campanha.status, lead.criado, lead.repassado, lead.status, mensagem.reacao, mensagem.editada, mensagem.apagada, mensagem.voto, contato.digitando, contato.presenca, contato.atualizado, chamada.recebida, chamada.encerrada, numero.banido, historico.sincronizado, grupo.atualizado, grupo.participantes, grupo.entrou, etiqueta.alterada, etiqueta.aplicada, canal.seguido, canal.deixado, evolution.bruto.

eventos[].descricaostringsim
eventos[].tiposNumeroenum[]sim

Tipos de número em que o evento ocorre.

eventos[].versaointegersim

Versão do formato de dados.

tiposDestinoenum[]sim
websocketobjectsim
websocket.urlstringsim

URL da conexão WebSocket de eventos.

Exemplo de resposta
{
  "eventos": [
    {
      "nome": "mensagem.recebida",
      "descricao": "texto",
      "tiposNumero": [
        "OFICIAL"
      ],
      "versao": 0
    }
  ],
  "tiposDestino": [
    "WEBHOOK"
  ],
  "websocket": {
    "url": "texto"
  }
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.
post /v1/eventos/ws/ticket

Ticket para o WebSocket

Ticket de uso único, válido por 60s, para abrir wss://api.zaphub.wssengenharia.com.br/v1/eventos/ws?ticket=<ticket> a partir do navegador (que não envia headers no WebSocket). Servidores podem conectar direto com o header Authorization: Bearer <chave>. A chave de API nunca vai na URL.

Depois de conectar, envie {"tipo":"assinar","eventos":[...],"numeroIds":[...],"desde":"<id do último evento>"} (todos os campos opcionais). O servidor responde {"tipo":"assinado"}, reenvia os eventos posteriores a desde (até 24h) e segue ao vivo, um envelope por mensagem. Ping a cada 25s; até 5 conexões por chave (a sexta fecha com o código 4429). Entrega "melhor esforço": para garantia, use um destino.

Resposta 201 · Criado

Ticket.

CampoTipoObrig.Descrição
ticketstringsim

Uso único. Conecte em /v1/eventos/ws?ticket=<ticket>.

expiraEmSegundosintegersim
Exemplo de resposta
{
  "ticket": "texto",
  "expiraEmSegundos": 0
}

Erros possíveis

  • 401 Não autenticado : Chave de API ausente, inválida ou revogada.
  • 402 Assinatura inativa : A assinatura da conta está inativa (suspensa ou cancelada). Regularize o pagamento.
  • 429 Limite de taxa : Limite de requisições por minuto da chave excedido. Aguarde `Retry-After` segundos.

Eventos

Escolha como receber: webhook HTTPS, RabbitMQ, NATS, Kafka ou Amazon SQS (sempre na sua própria infraestrutura), combinando quantos quiser. Cadastre até 5 destinos por conta, cada um com os eventos que assina e, se quiser, um filtro de números (painel ou /v1/destinos-eventos). Para tempo real sem cadastrar destino, abra o WebSocket. A lista de eventos também sai em GET /v1/eventos/catalogo.

Envelope

Todo evento usa o mesmo envelope JSON, no webhook e no RabbitMQ. O mesmo evento vai com o mesmo id para todos os destinos que o assinam e em cada nova tentativa: deduplique por ele.

CampoTipoObrig.Descrição
idstringsim

Único. Use para deduplicar (o mesmo evento vai com o mesmo id para todos os destinos e em cada nova tentativa).

eventoenumsim

Valores: mensagem.recebida, mensagem.enviada, mensagem.status, numero.status, numero.qrcode, contato.optout, campanha.status, lead.criado, lead.repassado, lead.status, mensagem.reacao, mensagem.editada, mensagem.apagada, mensagem.voto, contato.digitando, contato.presenca, contato.atualizado, chamada.recebida, chamada.encerrada, numero.banido, historico.sincronizado, grupo.atualizado, grupo.participantes, grupo.entrou, etiqueta.alterada, etiqueta.aplicada, canal.seguido, canal.deixado, evolution.bruto.

ocorridoEmstringsim
contaIdstringsim
numeroIdstring | nullsim

Nulo em eventos sem número.

sequenciaintegersim

Crescente por número: use para reordenar. Zero em eventos sem número.

dadosobjectsim

Varia por evento (ver cada evento).

Eventos de mensagem incluem tipoNumero, tipoEnvio, origem e contatoOptout; na saída de números não oficiais, também o custoEvitado em Marketing e em Utility.

Eventos e routing keys

No RabbitMQ a routing key é <prefixo>.<evento> (o prefixo é o que você definiu no destino; padrão zaphub). O teste de conexão publica teste.ping.

Evento Routing key (prefixo padrão) Quando
mensagem.recebida zaphub.mensagem.recebida

Mensagem recebida em um número.

mensagem.enviada zaphub.mensagem.enviada

Mensagem enviada (pela API, pelo painel, automática ou pelo celular).

mensagem.status zaphub.mensagem.status

Mudança de status (enviada, entregue, lida, falhou). Em falhou traz erro com o código e a mensagem da Meta.

numero.status zaphub.numero.status

Conectado, desconectado, erro, restrito…

numero.qrcode zaphub.numero.qrcode

QR renovado durante o pareamento (codigo é o texto do QR; qrcode, a imagem, quando disponível).

contato.optout zaphub.contato.optout

O contato pediu para não receber mais mensagens (SAIR, PARAR, STOP…).

campanha.status zaphub.campanha.status

Mudança de estado de uma campanha, com os totais.

lead.criado zaphub.lead.criado

Novo lead captado pelo número oficial.

lead.repassado zaphub.lead.repassado

Lead repassado a um número não oficial.

lead.status zaphub.lead.status

Mudança de status do lead (respondeu, sem resposta, descartado…).

mensagem.reacao zaphub.mensagem.reacao

Reação a uma mensagem, do contato ou do próprio número (emoji vazio = reação removida). mensagemId é nulo se a mensagem não está no histórico. Números oficiais e não oficiais.

mensagem.editada zaphub.mensagem.editada

O autor editou uma mensagem de texto (números não oficiais). A mensagem guardada passa a ter o texto novo. textoNovo é nulo quando a mensagem original não está no histórico (o WhatsApp manda a edição criptografada com a chave da original).

mensagem.apagada zaphub.mensagem.apagada

O autor apagou a mensagem para todos (números não oficiais). Texto e mídia são limpos; o download passa a responder 410.

mensagem.voto zaphub.mensagem.voto

Um contato votou, trocou o voto ou retirou o voto (opcoes vazio) numa enquete enviada pelo número (números não oficiais). O resultado consolidado está em GET /v1/mensagens/{id}/votos.

contato.digitando zaphub.contato.digitando

O contato está digitando, gravando áudio ou parou (estado). Efêmero: reenvio curto, sem caixa de entrada. Só é assinado no Evolution quando algum destino ou WebSocket o pede. O WhatsApp só informa a digitação a números que estão online e com conversa já registrada no ZapHub.

contato.presenca zaphub.contato.presenca

O contato ficou online ou offline (conforme a privacidade dele e só para contatos com conversa ativa). Efêmero.

contato.atualizado zaphub.contato.atualizado

O contato mudou nome público, foto ou recado. Só os campos que mudaram vêm preenchidos.

chamada.recebida zaphub.chamada.recebida

O número recebeu uma chamada. Chamadas recusadas automaticamente pela configuração do número não geram evento.

chamada.encerrada zaphub.chamada.encerrada

Uma chamada terminou (motivo como o WhatsApp informa).

numero.banido zaphub.numero.banido

O WhatsApp aplicou banimento temporário ao número. Também sai numero.status (DESCONECTADO, motivo banido) e o OWNER é avisado.

historico.sincronizado zaphub.historico.sincronizado

O celular concluiu uma etapa de sincronização de histórico. As mensagens do histórico não entram na caixa de entrada (assine evolution.bruto para o conteúdo). Efêmero.

grupo.atualizado zaphub.grupo.atualizado

Nome, descrição ou configurações de um grupo mudaram (no WhatsApp ou pela API).

grupo.participantes zaphub.grupo.participantes

Participantes entraram, saíram, foram promovidos ou rebaixados. numeroSaiu: true quando o próprio número saiu ou foi removido.

grupo.entrou zaphub.grupo.entrou

O número entrou num grupo (convite, link ou grupo criado por ele).

etiqueta.alterada zaphub.etiqueta.alterada

Uma etiqueta do WhatsApp Business foi criada, editada (acao: salva) ou removida (acao: removida), no celular ou pela API.

etiqueta.aplicada zaphub.etiqueta.aplicada

Uma etiqueta foi aplicada (aplicada: true) ou tirada de uma conversa ou mensagem pelo celular.

canal.seguido zaphub.canal.seguido

O número passou a seguir um canal (newsletter), pelo celular.

canal.deixado zaphub.canal.deixado

O número deixou de seguir um canal (newsletter).

evolution.bruto zaphub.evolution.bruto

Evento original do Evolution Go (números não oficiais), sem credenciais e sem base64 (midiaDisponivel: true). O formato de payload acompanha a versão do Evolution e pode mudar. Payload acima de 200 KB sai só com payloadTruncado: true.

teste.ping zaphub.teste.ping Enviado pelo botão "Testar" do destino. Não é assinável.

Campos de dados por evento

mensagem.recebida
{
  "id": "clx0evento00000000000000001",
  "evento": "mensagem.recebida",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "mensagemId": "clx0mensagem0000000000001",
    "conversaId": "clx0conversa0000000000001",
    "numero": "clx0numero000000000000001",
    "tipoNumero": "NAO_OFICIAL",
    "origem": "CONTATO",
    "direcao": "ENTRADA",
    "tipoConteudo": "TEXTO",
    "texto": "Oi, queria saber o preço",
    "midia": null,
    "localizacao": null,
    "contato": {
      "telefone": "5511987654321",
      "nome": "Ana"
    },
    "ocorridaEm": "2026-10-14T13:02:11.000Z"
  }
}
mensagem.enviada
{
  "id": "clx0evento00000000000000001",
  "evento": "mensagem.enviada",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "mensagemId": "clx0mensagem0000000000001",
    "conversaId": "clx0conversa0000000000001",
    "numeroId": "clx0numero000000000000001",
    "leadId": null,
    "para": "5511987654321",
    "tipoNumero": "NAO_OFICIAL",
    "tipoEnvio": "PRIMEIRO_CONTATO",
    "origem": "API",
    "tipoConteudo": "TEXTO",
    "texto": "Olá!",
    "idExterno": "3EB0A1B2C3",
    "enviadoEm": "2026-10-14T13:02:11.000Z",
    "enfileirada": false,
    "contatoOptout": false,
    "janelaAberta": false,
    "custoEvitado": {
      "marketingCentavos": 34,
      "utilityCentavos": 7
    }
  }
}
mensagem.status
{
  "id": "clx0evento00000000000000001",
  "evento": "mensagem.status",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "mensagemId": "clx0mensagem0000000000001",
    "numeroId": "clx0numero000000000000001",
    "status": "falhou",
    "ocorridoEm": "2026-10-14T13:02:12.000Z",
    "erro": {
      "codigo": "janela_fechada",
      "codigoExterno": "131047",
      "mensagem": "Passaram mais de 24 horas desde a última mensagem do contato. Envie um template aprovado para retomar a conversa."
    }
  }
}
numero.status
{
  "id": "clx0evento00000000000000001",
  "evento": "numero.status",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "numeroId": "clx0numero000000000000001",
    "tipo": "NAO_OFICIAL",
    "apelido": "Vendas",
    "status": "CONECTADO",
    "telefone": "5511987654321",
    "nomePerfil": "Loja Exemplo",
    "ocorridoEm": "2026-10-14T13:02:11.000Z"
  }
}
numero.qrcode
{
  "id": "clx0evento00000000000000001",
  "evento": "numero.qrcode",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "numeroId": "clx0numero000000000000001",
    "codigo": "2@abc…",
    "qrcode": "data:image/png;base64,…",
    "expiraEm": "2026-10-14T13:04:11.000Z"
  }
}
contato.optout
{
  "id": "clx0evento00000000000000001",
  "evento": "contato.optout",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "telefone": "5511987654321",
    "numero": "clx0numero000000000000001"
  }
}
campanha.status
{
  "id": "clx0evento00000000000000001",
  "evento": "campanha.status",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "campanhaId": "clx0campanha00000000000001",
    "nome": "Promo de outubro",
    "numeroId": "clx0numero000000000000001",
    "status": "CONCLUIDA",
    "motivo": null,
    "total": 3000,
    "taxaFalha": 0.4,
    "custoEstimadoCentavos": 10200,
    "ocorridoEm": "2026-10-14T13:20:00.000Z"
  }
}
lead.criado
{
  "id": "clx0evento00000000000000001",
  "evento": "lead.criado",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "leadId": "clx0lead000000000000000001",
    "status": "NOVO",
    "origem": "CAMPANHA",
    "contato": {
      "telefone": "5511987654321",
      "nome": "Ana"
    },
    "numeroOficialId": "clx0numero000000000000001",
    "numeroAtribuido": null,
    "campanha": {
      "id": "clx0campanha00000000000001",
      "nome": "Promo de outubro"
    },
    "primeiraMensagem": "Quero saber mais",
    "criadoEm": "2026-10-14T13:02:11.000Z"
  }
}
lead.repassado
{
  "id": "clx0evento00000000000000001",
  "evento": "lead.repassado",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "leadId": "clx0lead000000000000000001",
    "status": "REPASSADO",
    "statusAnterior": "NOVO",
    "origem": "CAMPANHA",
    "numeroAtribuido": {
      "id": "clx0numero000000000000001",
      "telefone": "5511987654321",
      "apelido": "Comercial"
    },
    "repassadoEm": "2026-10-14T13:03:00.000Z"
  }
}
lead.status
{
  "id": "clx0evento00000000000000001",
  "evento": "lead.status",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "leadId": "clx0lead000000000000000001",
    "status": "RESPONDEU",
    "statusAnterior": "REPASSADO",
    "respondeuEm": "2026-10-14T13:10:00.000Z"
  }
}
mensagem.reacao
{
  "id": "clx0evento00000000000000001",
  "evento": "mensagem.reacao",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "mensagemId": "clx0mensagem00000000000001",
    "idExternoAlvo": "3EB0C5A277F7F9B6C599",
    "conversaId": "clx0conversa00000000000001",
    "numero": "clx0numero000000000000001",
    "tipoNumero": "NAO_OFICIAL",
    "autor": "contato",
    "contato": {
      "telefone": "5511987654321"
    },
    "emoji": "👍",
    "ocorridaEm": "2026-10-14T13:05:00.000Z"
  }
}
mensagem.editada
{
  "id": "clx0evento00000000000000001",
  "evento": "mensagem.editada",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "mensagemId": "clx0mensagem00000000000001",
    "idExternoAlvo": "3EB0C5A277F7F9B6C599",
    "conversaId": "clx0conversa00000000000001",
    "numero": "clx0numero000000000000001",
    "tipoNumero": "NAO_OFICIAL",
    "autor": "contato",
    "contato": {
      "telefone": "5511987654321"
    },
    "textoNovo": "Quero 3 unidades",
    "editadaEm": "2026-10-14T13:06:00.000Z",
    "ocorridaEm": "2026-10-14T13:06:00.000Z"
  }
}
mensagem.apagada
{
  "id": "clx0evento00000000000000001",
  "evento": "mensagem.apagada",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "mensagemId": "clx0mensagem00000000000001",
    "idExternoAlvo": "3EB0C5A277F7F9B6C599",
    "conversaId": "clx0conversa00000000000001",
    "numero": "clx0numero000000000000001",
    "tipoNumero": "NAO_OFICIAL",
    "autor": "contato",
    "contato": {
      "telefone": "5511987654321"
    },
    "apagadaEm": "2026-10-14T13:07:00.000Z",
    "ocorridaEm": "2026-10-14T13:07:00.000Z"
  }
}
mensagem.voto
{
  "id": "clx0evento00000000000000001",
  "evento": "mensagem.voto",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "mensagemId": "clx0mensagem00000000000001",
    "conversaId": "clx0conversa00000000000001",
    "numero": "clx0numero000000000000001",
    "contato": {
      "telefone": "5511987654321"
    },
    "opcoes": [
      "Sexta-feira"
    ],
    "votadoEm": "2026-10-14T13:07:00.000Z"
  }
}
contato.digitando
{
  "id": "clx0evento00000000000000001",
  "evento": "contato.digitando",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "numero": "clx0numero000000000000001",
    "contato": {
      "telefone": "5511987654321"
    },
    "estado": "digitando"
  }
}
contato.presenca
{
  "id": "clx0evento00000000000000001",
  "evento": "contato.presenca",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "numero": "clx0numero000000000000001",
    "contato": {
      "telefone": "5511987654321"
    },
    "online": false,
    "vistoPorUltimoEm": "2026-10-14T12:58:00.000Z"
  }
}
contato.atualizado
{
  "id": "clx0evento00000000000000001",
  "evento": "contato.atualizado",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "numero": "clx0numero000000000000001",
    "conversaId": "clx0conversa00000000000001",
    "contato": {
      "telefone": "5511987654321"
    },
    "nome": "Carlos S."
  }
}
chamada.recebida
{
  "id": "clx0evento00000000000000001",
  "evento": "chamada.recebida",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "chamadaId": "B1C4F2A9E0",
    "numero": "clx0numero000000000000001",
    "contato": {
      "telefone": "5511987654321"
    },
    "video": false,
    "recebidaEm": "2026-10-14T13:08:00.000Z"
  }
}
chamada.encerrada
{
  "id": "clx0evento00000000000000001",
  "evento": "chamada.encerrada",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "chamadaId": "B1C4F2A9E0",
    "numero": "clx0numero000000000000001",
    "contato": {
      "telefone": "5511987654321"
    },
    "motivo": "timeout",
    "encerradaEm": "2026-10-14T13:08:40.000Z"
  }
}
numero.banido
{
  "id": "clx0evento00000000000000001",
  "evento": "numero.banido",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "numero": "clx0numero000000000000001",
    "codigo": "SENT_TO_TOO_MANY_PEOPLE",
    "expiraEm": "2026-10-15T13:00:00.000Z",
    "ocorridoEm": "2026-10-14T13:00:00.000Z"
  }
}
historico.sincronizado
{
  "id": "clx0evento00000000000000001",
  "evento": "historico.sincronizado",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "numero": "clx0numero000000000000001",
    "tipo": "RECENT",
    "progresso": 100,
    "conversas": 42,
    "mensagens": 1380
  }
}
grupo.atualizado
{
  "id": "clx0evento00000000000000001",
  "evento": "grupo.atualizado",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "numero": "clx0numero000000000000001",
    "grupo": {
      "id": "clx0grupo00000000000000001",
      "nome": "Clientes VIP"
    },
    "nome": "Clientes VIP",
    "descricao": "Ofertas da semana",
    "soAdminsEnviam": true,
    "soAdminsEditam": true,
    "alteradoPor": "5511987654321"
  }
}
grupo.participantes
{
  "id": "clx0evento00000000000000001",
  "evento": "grupo.participantes",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "numero": "clx0numero000000000000001",
    "grupo": {
      "id": "clx0grupo00000000000000001",
      "nome": "Clientes VIP"
    },
    "entraram": [
      "5511987654321"
    ],
    "sairam": [],
    "promovidos": [],
    "rebaixados": [],
    "numeroSaiu": false
  }
}
grupo.entrou
{
  "id": "clx0evento00000000000000001",
  "evento": "grupo.entrou",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "numero": "clx0numero000000000000001",
    "grupo": {
      "id": "clx0grupo00000000000000001",
      "nome": "Clientes VIP"
    },
    "motivo": "invite"
  }
}
etiqueta.alterada
{
  "id": "clx0evento00000000000000001",
  "evento": "etiqueta.alterada",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "numero": "clx0numero000000000000001",
    "etiqueta": {
      "id": "clx0etiqueta0000000000001",
      "nome": "Pagamento pendente",
      "cor": 3
    },
    "acao": "salva"
  }
}
etiqueta.aplicada
{
  "id": "clx0evento00000000000000001",
  "evento": "etiqueta.aplicada",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "numero": "clx0numero000000000000001",
    "etiqueta": {
      "id": "clx0etiqueta0000000000001",
      "nome": "Pagamento pendente",
      "cor": 3
    },
    "alvo": "conversa",
    "aplicada": true,
    "conversaId": "clx0conversa00000000000001",
    "mensagemId": null,
    "contato": {
      "telefone": "5511987654321"
    }
  }
}
canal.seguido
{
  "id": "clx0evento00000000000000001",
  "evento": "canal.seguido",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "numero": "clx0numero000000000000001",
    "canal": {
      "id": "120363100000000001@newsletter",
      "nome": "Ofertas da Loja"
    },
    "papel": "subscriber"
  }
}
canal.deixado
{
  "id": "clx0evento00000000000000001",
  "evento": "canal.deixado",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "numero": "clx0numero000000000000001",
    "canal": {
      "id": "120363100000000001@newsletter",
      "nome": null
    },
    "papel": "guest"
  }
}
evolution.bruto
{
  "id": "clx0evento00000000000000001",
  "evento": "evolution.bruto",
  "ocorridoEm": "2026-10-14T13:02:11.000Z",
  "contaId": "clx0conta000000000000000001",
  "numeroId": "clx0numero000000000000001",
  "sequencia": 4812,
  "dados": {
    "numero": "clx0numero000000000000001",
    "eventoOriginal": "Message",
    "payload": {
      "Info": {
        "ID": "3EB0C5A277F7F9B6C599",
        "Chat": "5511987654321@s.whatsapp.net"
      },
      "Message": {
        "conversation": "Olá!"
      }
    }
  }
}

Webhooks usam URL HTTPS (endereços privados ou de loopback são recusados, também a cada entrega) e respondem 2xx para confirmar. Falhou? Reenviamos com backoff exponencial, até 6 tentativas em 24h; depois a entrega fica FALHOU no log (GET /v1/destinos-eventos/{id}/entregas), com reenvio manual.

Verificação de assinatura

Cada entrega traz a assinatura HMAC-SHA256 calculada com o segredo do destino (segredoHmacRevelado, exibido na criação). O formato é t=<unix>,v1=<hex>, onde v1 é o HMAC de "<t>." + corpo cru. Confira sobre o corpo cru (antes de decodificar o JSON) e compare em tempo constante.

  • Webhook: header X-ZapHub-Signature. Recuse timestamps fora da tolerância (padrão: 5 minutos) para evitar replay.
  • RabbitMQ, NATS e Kafka: header x-zaphub-signature; SQS: atributo assinatura (mesmo formato e mesmo segredo). De fila ou stream, confira só o HMAC, sem tolerância de tempo: a mensagem pode ter esperado horas.
import { createHmac, timingSafeEqual } from "node:crypto";

// Header X-ZapHub-Signature: t=1760446931,v1=<hex>
// v1 = HMAC-SHA256(segredo, "<t>." + corpo CRU). O segredo é o "segredoHmacRevelado" do destino.
export function assinaturaValida(segredo, corpoCru, header, toleranciaS = 300) {
  const campos = Object.fromEntries(String(header ?? "").split(",").map((p) => p.trim().split("=")));
  if (!/^\d+$/.test(campos.t ?? "") || !/^[0-9a-f]{64}$/i.test(campos.v1 ?? "")) return false;
  // Webhook: recuse timestamps fora da tolerância (replay). Fila (RabbitMQ): passe null.
  if (toleranciaS !== null && Math.abs(Date.now() / 1000 - Number(campos.t)) > toleranciaS) return false;
  const esperado = createHmac("sha256", segredo).update(campos.t + "." + corpoCru).digest();
  const recebido = Buffer.from(campos.v1, "hex");
  return recebido.length === esperado.length && timingSafeEqual(recebido, esperado);
}

// Express: use o corpo CRU (express.raw), nunca o JSON já reserializado.
// app.post("/hooks/zaphub", express.raw({ type: "application/json" }), (req, res) => {
//   const corpo = req.body.toString("utf8");
//   if (!assinaturaValida(process.env.ZAPHUB_SEGREDO, corpo, req.get("x-zaphub-signature"))) return res.sendStatus(401);
//   const evento = JSON.parse(corpo);          // deduplique por evento.id
//   res.sendStatus(200);                       // qualquer 2xx confirma a entrega
// });
<?php
// Header X-ZapHub-Signature: t=1760446931,v1=<hex>
// v1 = HMAC-SHA256(segredo, "<t>." . corpo CRU). O segredo é o "segredoHmacRevelado" do destino.
function assinaturaValida(string $segredo, string $corpoCru, ?string $header, ?int $toleranciaS = 300): bool
{
    $campos = [];
    foreach (explode(',', (string) $header) as $parte) {
        [$k, $v] = array_pad(explode('=', trim($parte), 2), 2, '');
        $campos[$k] = $v;
    }
    $t = $campos['t'] ?? '';
    $v1 = $campos['v1'] ?? '';
    if (!ctype_digit($t) || !preg_match('/^[0-9a-f]{64}$/i', $v1)) return false;
    // Webhook: recuse timestamps fora da tolerância (replay). Fila (RabbitMQ): passe null.
    if ($toleranciaS !== null && abs(time() - (int) $t) > $toleranciaS) return false;
    $esperado = hash_hmac('sha256', $t . '.' . $corpoCru, $segredo);
    return hash_equals($esperado, strtolower($v1));
}

// Receptor do webhook (corpo CRU, não o JSON decodificado)
$corpo = file_get_contents('php://input');
if (!assinaturaValida(getenv('ZAPHUB_SEGREDO'), $corpo, $_SERVER['HTTP_X_ZAPHUB_SIGNATURE'] ?? null)) {
    http_response_code(401);
    exit;
}
$evento = json_decode($corpo, true);   // deduplique por $evento['id']
http_response_code(200);               // qualquer 2xx confirma a entrega

RabbitMQ

O ZapHub publica no seu broker (ele só publica; a fila e os bindings são seus). Informe host, porta, vhost, usuário e senha (ou uma URI amqps://), a exchange e um prefixo de routing key. A exchange é do tipo topic (criada como durable se não existir). Cada evento é publicado assim:

  • routing key <prefixo>.<evento> (ex.: zaphub.mensagem.recebida), mensagem persistente (delivery_mode = 2) e content_type = application/json;
  • message_id igual ao id do evento; headers x-zaphub-evento, x-zaphub-conta e x-zaphub-signature;
  • com confirmação do broker (publisher confirms): se não confirmar em 10 segundos, o evento é republicado com backoff, até 6 tentativas em 24h, na ordem de sequencia;
  • TLS (amqps) por padrão; amqp sem TLS exige confirmação explícita; host interno é recusado (a checagem se repete a cada conexão);
  • sem fila ligada à routing key, a entrega fica NAO_ROTEADA e o painel avisa;
  • modo filas ("modo": "filas"): o ZapHub declara uma fila durável por evento assinado (<prefixo>.<evento>) já ligada à exchange, como o AMQP_SPECIFIC_EVENTS do Evolution. Filas de eventos que você tirar do filtro não são apagadas (podem ter mensagens ainda não consumidas).
import amqp from "amqplib";
import { assinaturaValida } from "./assinatura.js";

const conexao = await amqp.connect(process.env.AMQP_URL); // amqps://usuario:senha@mq.exemplo.com.br:5671/
const canal = await conexao.createChannel();

// A fila e o binding são SEUS: o ZapHub só publica na exchange (topic) do destino.
await canal.assertQueue("zaphub.mensagens", { durable: true });
await canal.bindQueue("zaphub.mensagens", "zaphub", "zaphub.mensagem.*"); // <prefixo>.<evento>
await canal.prefetch(20);

await canal.consume("zaphub.mensagens", (m) => {
  if (!m) return;
  const corpo = m.content.toString("utf8");
  // Da fila, confira só o HMAC (toleranciaS = null): a mensagem pode ter esperado na fila.
  if (!assinaturaValida(process.env.ZAPHUB_SEGREDO, corpo, m.properties.headers["x-zaphub-signature"], null)) {
    return canal.nack(m, false, false);
  }
  const evento = JSON.parse(corpo); // m.properties.messageId === evento.id
  // ... processe e deduplique por evento.id (a entrega é "pelo menos uma vez")
  canal.ack(m);
});
<?php
// composer require php-amqplib/php-amqplib
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/assinatura.php';

use PhpAmqpLib\Connection\AMQPSSLConnection;
use PhpAmqpLib\Message\AMQPMessage;

$conexao = new AMQPSSLConnection('mq.exemplo.com.br', 5671, 'zaphub', getenv('AMQP_SENHA'), '/', ['verify_peer' => true]);
$canal = $conexao->channel();

// A fila e o binding são SEUS: o ZapHub só publica na exchange (topic) do destino.
$canal->queue_declare('zaphub.mensagens', false, true, false, false);
$canal->queue_bind('zaphub.mensagens', 'zaphub', 'zaphub.mensagem.*'); // <prefixo>.<evento>
$canal->basic_qos(null, 20, null);

$canal->basic_consume('zaphub.mensagens', '', false, false, false, false, function (AMQPMessage $m) {
    $corpo = $m->getBody();
    $headers = $m->get('application_headers')->getNativeData();
    // Da fila, confira só o HMAC ($toleranciaS = null): a mensagem pode ter esperado na fila.
    if (!assinaturaValida(getenv('ZAPHUB_SEGREDO'), $corpo, $headers['x-zaphub-signature'] ?? null, null)) {
        $m->nack(false, false);
        return;
    }
    $evento = json_decode($corpo, true);   // $m->get('message_id') === $evento['id']
    // ... processe e deduplique por $evento['id'] (a entrega é "pelo menos uma vez")
    $m->ack();
});

while ($canal->is_consuming()) {
    $canal->wait();
}

A entrega é "pelo menos uma vez": deduplique por id do evento e reordene por sequencia (crescente por número) se precisar.

NATS, Kafka e SQS

Mesmo envelope, mesma assinatura, mesma ordem por número e mesmo reenvio (até 6 tentativas em 24h). TLS por padrão e hosts internos recusados.

  • NATS: assunto <prefixo>.<contaId>.<numeroId>.<evento> (eventos sem número usam conta), então zaphub.*.NUMERO.> pega tudo de um número. Autenticação por token, usuário e senha ou arquivo .creds. Com jetstream: true, a publicação espera o ack do stream e é deduplicada pelo Nats-Msg-Id (igual ao id do evento); crie um stream que cubra o prefixo.
  • Kafka: um tópico único (topico) ou um por evento (prefixoTopico → zaphub-mensagem-recebida). A chave da mensagem é o numeroId: eventos do mesmo número caem na mesma partição, em ordem. Produtor idempotente com acks=all; SASL PLAIN ou SCRAM. O ZapHub não cria tópicos.
  • Amazon SQS: informe a URL da fila e uma chave de acesso com sqs:SendMessage. Em fila .fifo, o MessageGroupId é o número e o MessageDeduplicationId é o id do evento (reenvio não duplica). Em fila padrão, reordene por sequencia. Eventos acima de 256 KB não cabem no SQS.
import { connect } from "nats"; // npm i nats
import { assinaturaValida } from "./assinatura.js";

const nc = await connect({ servers: "tls://nats.exemplo.com.br:4222", token: process.env.NATS_TOKEN });
// <prefixo>.<contaId>.<numeroId>.<evento>: ">" pega tudo; "zaphub.*.NUMERO.>" só um número.
for await (const m of nc.subscribe("zaphub.>")) {
  const corpo = new TextDecoder().decode(m.data);
  if (!assinaturaValida(process.env.ZAPHUB_SEGREDO, corpo, m.headers?.get("x-zaphub-signature"), null)) continue;
  const evento = JSON.parse(corpo); // m.headers.get("Nats-Msg-Id") === evento.id
  console.log(evento.evento, evento.dados);
}
import { Kafka } from "kafkajs"; // npm i kafkajs
import { assinaturaValida } from "./assinatura.js";

const kafka = new Kafka({
  brokers: ["kafka.exemplo.com.br:9093"],
  ssl: true,
  sasl: { mechanism: "scram-sha-512", username: "zaphub", password: process.env.KAFKA_SENHA },
});
const consumer = kafka.consumer({ groupId: "meu-sistema" });
await consumer.connect();
await consumer.subscribe({ topics: ["zaphub-mensagem-recebida", "zaphub-mensagem-status"] });
await consumer.run({
  // A chave da mensagem é o numeroId: a ordem por número vem da partição.
  eachMessage: async ({ message }) => {
    const corpo = message.value.toString("utf8");
    const assinatura = message.headers["x-zaphub-signature"]?.toString();
    if (!assinaturaValida(process.env.ZAPHUB_SEGREDO, corpo, assinatura, null)) return;
    const evento = JSON.parse(corpo); // deduplique por evento.id
    console.log(evento.evento, evento.dados);
  },
});
<?php
// pecl install rdkafka
require __DIR__ . '/assinatura.php';

$conf = new RdKafka\Conf();
$conf->set('bootstrap.servers', 'kafka.exemplo.com.br:9093');
$conf->set('security.protocol', 'SASL_SSL');
$conf->set('sasl.mechanisms', 'SCRAM-SHA-512');
$conf->set('sasl.username', 'zaphub');
$conf->set('sasl.password', getenv('KAFKA_SENHA'));
$conf->set('group.id', 'meu-sistema');

$consumer = new RdKafka\KafkaConsumer($conf);
$consumer->subscribe(['zaphub-mensagem-recebida', 'zaphub-mensagem-status']);
while (true) {
    $m = $consumer->consume(1000);
    if ($m->err !== RD_KAFKA_RESP_ERR_NO_ERROR) continue;
    $assinatura = $m->headers['x-zaphub-signature'] ?? null;
    if (!assinaturaValida(getenv('ZAPHUB_SEGREDO'), $m->payload, $assinatura, null)) continue;
    $evento = json_decode($m->payload, true);   // deduplique por $evento['id']
}
import { SQSClient, ReceiveMessageCommand, DeleteMessageCommand } from "@aws-sdk/client-sqs";
import { assinaturaValida } from "./assinatura.js";

const sqs = new SQSClient({ region: "sa-east-1" });
const QueueUrl = "https://sqs.sa-east-1.amazonaws.com/123456789012/zaphub.fifo";

for (;;) {
  const { Messages = [] } = await sqs.send(
    new ReceiveMessageCommand({ QueueUrl, MaxNumberOfMessages: 10, WaitTimeSeconds: 20, MessageAttributeNames: ["All"] }),
  );
  for (const m of Messages) {
    const assinatura = m.MessageAttributes?.assinatura?.StringValue;
    if (assinaturaValida(process.env.ZAPHUB_SEGREDO, m.Body, assinatura, null)) {
      const evento = JSON.parse(m.Body); // deduplique por evento.id
      console.log(evento.evento, evento.dados);
    }
    await sqs.send(new DeleteMessageCommand({ QueueUrl, ReceiptHandle: m.ReceiptHandle }));
  }
}
<?php
// composer require aws/aws-sdk-php
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/assinatura.php';

$sqs = new Aws\Sqs\SqsClient(['region' => 'sa-east-1', 'version' => 'latest']);
$fila = 'https://sqs.sa-east-1.amazonaws.com/123456789012/zaphub.fifo';

while (true) {
    $r = $sqs->receiveMessage(['QueueUrl' => $fila, 'MaxNumberOfMessages' => 10, 'WaitTimeSeconds' => 20, 'MessageAttributeNames' => ['All']]);
    foreach ($r->get('Messages') ?? [] as $m) {
        $assinatura = $m['MessageAttributes']['assinatura']['StringValue'] ?? null;
        if (assinaturaValida(getenv('ZAPHUB_SEGREDO'), $m['Body'], $assinatura, null)) {
            $evento = json_decode($m['Body'], true);   // deduplique por $evento['id']
        }
        $sqs->deleteMessage(['QueueUrl' => $fila, 'ReceiptHandle' => $m['ReceiptHandle']]);
    }
}

WebSocket

Para tempo real sem cadastrar destino: wss://api.zaphub.wssengenharia.com.br/v1/eventos/ws. Servidores conectam com o header Authorization: Bearer <chave>; no navegador, peça um ticket de uso único (POST /v1/eventos/ws/ticket, válido por 60s) e conecte com ?ticket=. A chave nunca vai na URL.

  • Depois de conectar, envie {"tipo":"assinar","eventos":[...],"numeroIds":[...],"desde":"<id>"} (tudo opcional).
  • Com desde (o id do último evento que você recebeu), o servidor reenvia o que passou depois dele, até 24h, e segue ao vivo.
  • Um envelope por mensagem, o mesmo dos destinos. Ping a cada 25s; até 5 conexões por chave (a sexta fecha com o código 4429).
  • É "melhor esforço": se precisar de garantia de entrega, use um destino (webhook, filas, streams).
// Node 22+ (WebSocket global) ou navegador. No navegador, troque o header por ?ticket=.
let ultimoId = null; // guarde o id do último evento para retomar após queda

function conectar() {
  const ws = new WebSocket("wss://api.zaphub.wssengenharia.com.br/v1/eventos/ws", {
    headers: { Authorization: "Bearer " + process.env.ZAPHUB_KEY },
  });
  ws.onopen = () =>
    ws.send(JSON.stringify({ tipo: "assinar", eventos: ["mensagem.recebida"], ...(ultimoId ? { desde: ultimoId } : {}) }));
  ws.onmessage = (m) => {
    const msg = JSON.parse(m.data);
    if (msg.tipo === "erro") return console.error(msg.codigo, msg.mensagem);
    if (msg.tipo) return; // "assinado"
    ultimoId = msg.id; // envelope do evento
    console.log(msg.evento, msg.dados);
  };
  ws.onclose = () => setTimeout(conectar, 2000); // retoma a partir de ultimoId
}
conectar();