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á recebe401). 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 responde404. Chaves restritas não gerenciam destinos de eventos (configuração da conta inteira). - Conta com assinatura suspensa ou cancelada:
402em 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-RemainingeX-RateLimit-Reset(epoch, em segundos). - Acima disso:
429comRetry-After(segundos). - Não há limite por volume de mensagens. Números
NAO_OFICIALsão cadenciados com fila no ritmo do número (enfileirada: true, nunca 429); osOFICIALseguem 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
422idempotencia_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)."
]
}
} | HTTP | code | Quando |
|---|---|---|
400 | validacao | Corpo ou parâmetros inválidos. O objeto fields diz o que corrigir. |
400 | destino_invalido | Destino de evento recusado: URL sem HTTPS, host interno/privado, broker sem TLS... |
401 | nao_autenticado | Chave de API ausente, malformada, desconhecida ou revogada. |
402 | assinatura_inativa | A assinatura da conta está suspensa ou cancelada. Regularize o pagamento no painel. |
403 | sem_permissao | A chave é restrita a números e o recurso está fora do conjunto dela. |
404 | nao_encontrado | O recurso não existe ou é de outra conta (nunca confirmamos a existência). |
409 | conflito | Estado incompatível: pausar campanha que não está em andamento, repassar lead já repassado... |
409 | numero_indisponivel | Número não oficial desconectado. Reconecte-o; o ZapHub não troca de número sozinho. |
422 | regra_negocio | Regra de negócio, ex.: conectar um número OFICIAL (use as credenciais da Meta no painel). |
422 | campanha_exige_api_oficial | Campanhas só saem por números OFICIAL. |
422 | template_exige_api_oficial | Templates existem só em números OFICIAL. |
422 | midia_url_invalida | URL da mídia sem HTTPS, com usuário/senha ou que aponta para host interno/IP privado. |
422 | recurso_indisponivel | O recurso não existe no tipo do número (ex.: enquete no oficial). fields.recurso diz qual; veja a matriz de recursos. |
422 | idempotencia_corpo_diferente | A mesma Idempotency-Key já foi usada com outro corpo. |
429 | limite_taxa | Mais de 600 requisições por minuto na chave. Aguarde Retry-After segundos. |
502/503 | evolution_indisponivel · meta_erro | O serviço de conexão do WhatsApp ou a Meta não respondeu. Tente novamente. |
502 | código do canal | Reagir, editar, apagar, marcar como lida ou digitando: o WhatsApp recusou a operação e nada mudou. |
500 | erro_interno | Erro 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.
| Recurso | recurso | Não oficial | Oficial |
|---|---|---|---|
| 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).
- get
/v1/numeros - get
/v1/numeros/{id} - post
/v1/numeros/{id}/conectar - post
/v1/numeros/{id}/desconectar
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");
/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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeros | object[] | sim | |
numeros[].id | string | sim | |
numeros[].tipo | enum | sim | Valores: |
numeros[].apelido | string | sim | |
numeros[].status | enum | sim | Valores: |
numeros[].telefone | string | null | sim | E.164 sem |
numeros[].nomePerfil | string | null | sim | |
numeros[].ritmoPorMinuto | integer | null | sim | Só NAO_OFICIAL: cadência de saída (mensagens/minuto). |
numeros[].criadoEm | string | sim | |
numeros[].ultimaAtividadeEm | string | null | sim | |
numeros[].oficial | object | null | sim | |
numeros[].oficial.qualityRating | string | null | sim | |
numeros[].oficial.nomeVerificado | string | null | sim | |
numeros[].oficial.alertaPagamento | boolean | sim | |
numeros[].oficial.statusMotivo | string | null | sim | |
numeros[].oficial.vazaoPorSegundo | integer | sim | |
numeros[].naoOficial | object | null | sim | |
numeros[].naoOficial.conectadoEm | string | null | sim | |
numeros[].naoOficial.protegidoPorProxy | boolean | sim | |
numeros[].indicadores | object | não | Só em |
numeros[].indicadores.numeroId | string | sim | |
numeros[].indicadores.tipo | enum | sim | Valores: |
numeros[].indicadores.geradoEm | string | sim | |
numeros[].indicadores.iniciosConversa | object | sim | |
numeros[].indicadores.iniciosConversa.ultimaHora | integer | sim | |
numeros[].indicadores.iniciosConversa.hoje | integer | sim | |
numeros[].indicadores.mensagensEnviadas | object | sim | |
numeros[].indicadores.mensagensEnviadas.ultimaHora | integer | sim | |
numeros[].indicadores.mensagensEnviadas.hoje | integer | sim | |
numeros[].indicadores.desconexoes | object | sim | |
numeros[].indicadores.desconexoes.ultimas24h | integer | sim | |
numeros[].indicadores.desconexoes.ultimaEm | string | null | sim | |
numeros[].indicadores.ritmoPorMinuto | integer | null | sim | |
numeros[].indicadores.naFila | integer | sim | |
numeros[].recursos | enum[] | sim | O que o número suporta, conforme o tipo. Rota fora da lista responde 422 |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador público do número. |
Resposta 200 · OK
O número.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
tipo | enum | sim | Valores: |
apelido | string | sim | |
status | enum | sim | Valores: |
telefone | string | null | sim | E.164 sem |
nomePerfil | string | null | sim | |
ritmoPorMinuto | integer | null | sim | Só NAO_OFICIAL: cadência de saída (mensagens/minuto). |
criadoEm | string | sim | |
ultimaAtividadeEm | string | null | sim | |
oficial | object | null | sim | |
oficial.qualityRating | string | null | sim | |
oficial.nomeVerificado | string | null | sim | |
oficial.alertaPagamento | boolean | sim | |
oficial.statusMotivo | string | null | sim | |
oficial.vazaoPorSegundo | integer | sim | |
naoOficial | object | null | sim | |
naoOficial.conectadoEm | string | null | sim | |
naoOficial.protegidoPorProxy | boolean | sim | |
indicadores | object | não | Só em |
indicadores.numeroId | string | sim | |
indicadores.tipo | enum | sim | Valores: |
indicadores.geradoEm | string | sim | |
indicadores.iniciosConversa | object | sim | |
indicadores.iniciosConversa.ultimaHora | integer | sim | |
indicadores.iniciosConversa.hoje | integer | sim | |
indicadores.mensagensEnviadas | object | sim | |
indicadores.mensagensEnviadas.ultimaHora | integer | sim | |
indicadores.mensagensEnviadas.hoje | integer | sim | |
indicadores.desconexoes | object | sim | |
indicadores.desconexoes.ultimas24h | integer | sim | |
indicadores.desconexoes.ultimaEm | string | null | sim | |
indicadores.ritmoPorMinuto | integer | null | sim | |
indicadores.naFila | integer | sim | |
recursos | enum[] | sim | O que o número suporta, conforme o tipo. Rota fora da lista responde 422 |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador público do número. |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
telefone | string | nã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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numero | object | sim | |
numero.id | string | sim | |
numero.tipo | enum | sim | Valores: |
numero.apelido | string | sim | |
numero.status | enum | sim | Valores: |
numero.telefone | string | null | sim | E.164 sem |
numero.nomePerfil | string | null | sim | |
numero.ritmoPorMinuto | integer | null | sim | Só NAO_OFICIAL: cadência de saída (mensagens/minuto). |
numero.criadoEm | string | sim | |
numero.ultimaAtividadeEm | string | null | sim | |
numero.oficial | object | null | sim | |
numero.oficial.qualityRating | string | null | sim | |
numero.oficial.nomeVerificado | string | null | sim | |
numero.oficial.alertaPagamento | boolean | sim | |
numero.oficial.statusMotivo | string | null | sim | |
numero.oficial.vazaoPorSegundo | integer | sim | |
numero.naoOficial | object | null | sim | |
numero.naoOficial.conectadoEm | string | null | sim | |
numero.naoOficial.protegidoPorProxy | boolean | sim | |
numero.indicadores | object | não | Só em |
numero.indicadores.numeroId | string | sim | |
numero.indicadores.tipo | enum | sim | Valores: |
numero.indicadores.geradoEm | string | sim | |
numero.indicadores.iniciosConversa | object | sim | |
numero.indicadores.iniciosConversa.ultimaHora | integer | sim | |
numero.indicadores.iniciosConversa.hoje | integer | sim | |
numero.indicadores.mensagensEnviadas | object | sim | |
numero.indicadores.mensagensEnviadas.ultimaHora | integer | sim | |
numero.indicadores.mensagensEnviadas.hoje | integer | sim | |
numero.indicadores.desconexoes | object | sim | |
numero.indicadores.desconexoes.ultimas24h | integer | sim | |
numero.indicadores.desconexoes.ultimaEm | string | null | sim | |
numero.indicadores.ritmoPorMinuto | integer | null | sim | |
numero.indicadores.naFila | integer | sim | |
numero.recursos | enum[] | sim | O que o número suporta, conforme o tipo. Rota fora da lista responde 422 |
modo | enum | sim | Valores: |
qrcode | string | null | sim | Imagem do QR (data URL PNG). Pode ser nula se o primeiro QR ainda não foi gerado: ele chega pelo evento |
codigo | string | null | sim | Texto do QR, para renderizar no seu sistema. |
codigoPareamento | string | null | sim | |
expiraEm | string | sim | Depois disso o QR deixa de ser renovado; chame |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador público do número. |
Resposta 200 · OK
O número desconectado.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
tipo | enum | sim | Valores: |
apelido | string | sim | |
status | enum | sim | Valores: |
telefone | string | null | sim | E.164 sem |
nomePerfil | string | null | sim | |
ritmoPorMinuto | integer | null | sim | Só NAO_OFICIAL: cadência de saída (mensagens/minuto). |
criadoEm | string | sim | |
ultimaAtividadeEm | string | null | sim | |
oficial | object | null | sim | |
oficial.qualityRating | string | null | sim | |
oficial.nomeVerificado | string | null | sim | |
oficial.alertaPagamento | boolean | sim | |
oficial.statusMotivo | string | null | sim | |
oficial.vazaoPorSegundo | integer | sim | |
naoOficial | object | null | sim | |
naoOficial.conectadoEm | string | null | sim | |
naoOficial.protegidoPorProxy | boolean | sim | |
indicadores | object | não | Só em |
indicadores.numeroId | string | sim | |
indicadores.tipo | enum | sim | Valores: |
indicadores.geradoEm | string | sim | |
indicadores.iniciosConversa | object | sim | |
indicadores.iniciosConversa.ultimaHora | integer | sim | |
indicadores.iniciosConversa.hoje | integer | sim | |
indicadores.mensagensEnviadas | object | sim | |
indicadores.mensagensEnviadas.ultimaHora | integer | sim | |
indicadores.mensagensEnviadas.hoje | integer | sim | |
indicadores.desconexoes | object | sim | |
indicadores.desconexoes.ultimas24h | integer | sim | |
indicadores.desconexoes.ultimaEm | string | null | sim | |
indicadores.ritmoPorMinuto | integer | null | sim | |
indicadores.naFila | integer | sim | |
recursos | enum[] | sim | O que o número suporta, conforme o tipo. Rota fora da lista responde 422 |
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.
- post
/v1/mensagens/texto - post
/v1/mensagens/midia - post
/v1/mensagens/localizacao - post
/v1/mensagens/contato - post
/v1/mensagens/figurinha - post
/v1/mensagens/interativa - post
/v1/mensagens/enquete - post
/v1/mensagens/template - get
/v1/mensagens/{id} - patch
/v1/mensagens/{id} - delete
/v1/mensagens/{id} - post
/v1/mensagens/{id}/reacao - get
/v1/mensagens/{id}/votos - post
/v1/mensagens/{id}/lida - post
/v1/conversas/presenca - get
/v1/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);
/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
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
para | string | não | Telefone do contato com DDI e DDD. Aceita máscara ( |
texto | string | sim | Tamanho: 1 a 4096. |
previaLink | boolean | não | Pede a prévia do primeiro link do texto. |
respondendoA | string | não |
|
mencoes | string[] | não | Telefones mencionados (só em grupos; em conversa individual é aceito e ignorado). Só NAO_OFICIAL. No máximo 50 itens. |
grupoId | string | não | Envia ao grupo ( |
mencionarTodos | boolean | nã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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da mensagem ( |
tipoNumero | enum | sim | Valores: |
tipoEnvio | enum | sim | Valores: |
status | enum | sim | Valores: |
enfileirada | boolean | sim | true = na fila de cadência do número (não oficial); sai logo em seguida. |
custoEvitado | object | null | sim | Só números NAO_OFICIAL: quanto custaria a mesma mensagem na API Oficial. |
custoEvitado.marketingCentavos | integer | sim | |
custoEvitado.utilityCentavos | integer | sim | |
contatoOptout | boolean | sim | O contato pediu para sair; o envio não é bloqueado, só sinalizado. |
janelaAberta | boolean | sim | Janela de 24h aberta (informativo). |
erro | object | não | Só com |
erro.codigo | string | sim | |
erro.codigoExterno | string | null | sim | |
erro.mensagem | string | sim | |
custoEstimado | object | null | não | Só em templates: estimativa pela tabela pública. |
custoEstimado.categoria | enum | sim | Valores: |
custoEstimado.centavos | integer | sim | Estimativa do que a Meta cobra por esta mensagem, em centavos de BRL. |
custoEstimado.precoUsd | string | sim | |
custoEstimado.cambio | string | sim | |
custoEstimado.tabelaVersao | integer | sim | |
custoEstimado.estimado | enum | sim | Valores: |
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.
/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
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
para | string | não | Telefone do contato com DDI e DDD. Aceita máscara ( |
tipo | enum | sim | Valores: |
url | string | não | URL HTTPS pública. http, hosts internos e IPs privados/loopback são recusados (422). Tamanho: 1 a 2048. |
base64 | string | não | Conteúdo em base64 (ou data URL). Alternativa a |
mimeType | string | não | Obrigatório com |
legenda | string | não | Tamanho: 0 a 1024. |
nomeArquivo | string | não | Nome exibido para documentos. Tamanho: 1 a 255. |
respondendoA | string | não |
|
mencoes | string[] | não | Telefones mencionados (só em grupos; em conversa individual é aceito e ignorado). Só NAO_OFICIAL. No máximo 50 itens. |
grupoId | string | não | Envia ao grupo ( |
mencionarTodos | boolean | nã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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da mensagem ( |
tipoNumero | enum | sim | Valores: |
tipoEnvio | enum | sim | Valores: |
status | enum | sim | Valores: |
enfileirada | boolean | sim | true = na fila de cadência do número (não oficial); sai logo em seguida. |
custoEvitado | object | null | sim | Só números NAO_OFICIAL: quanto custaria a mesma mensagem na API Oficial. |
custoEvitado.marketingCentavos | integer | sim | |
custoEvitado.utilityCentavos | integer | sim | |
contatoOptout | boolean | sim | O contato pediu para sair; o envio não é bloqueado, só sinalizado. |
janelaAberta | boolean | sim | Janela de 24h aberta (informativo). |
erro | object | não | Só com |
erro.codigo | string | sim | |
erro.codigoExterno | string | null | sim | |
erro.mensagem | string | sim | |
custoEstimado | object | null | não | Só em templates: estimativa pela tabela pública. |
custoEstimado.categoria | enum | sim | Valores: |
custoEstimado.centavos | integer | sim | Estimativa do que a Meta cobra por esta mensagem, em centavos de BRL. |
custoEstimado.precoUsd | string | sim | |
custoEstimado.cambio | string | sim | |
custoEstimado.tabelaVersao | integer | sim | |
custoEstimado.estimado | enum | sim | Valores: |
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.
/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
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
para | string | não | Telefone do contato com DDI e DDD. Aceita máscara ( |
latitude | number | sim | Intervalo: -90 a 90. |
longitude | number | sim | Intervalo: -180 a 180. |
nome | string | não | Tamanho: 0 a 256. |
endereco | string | não | Tamanho: 0 a 512. |
respondendoA | string | não |
|
mencoes | string[] | não | Telefones mencionados (só em grupos; em conversa individual é aceito e ignorado). Só NAO_OFICIAL. No máximo 50 itens. |
grupoId | string | não | Envia ao grupo ( |
mencionarTodos | boolean | nã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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da mensagem ( |
tipoNumero | enum | sim | Valores: |
tipoEnvio | enum | sim | Valores: |
status | enum | sim | Valores: |
enfileirada | boolean | sim | true = na fila de cadência do número (não oficial); sai logo em seguida. |
custoEvitado | object | null | sim | Só números NAO_OFICIAL: quanto custaria a mesma mensagem na API Oficial. |
custoEvitado.marketingCentavos | integer | sim | |
custoEvitado.utilityCentavos | integer | sim | |
contatoOptout | boolean | sim | O contato pediu para sair; o envio não é bloqueado, só sinalizado. |
janelaAberta | boolean | sim | Janela de 24h aberta (informativo). |
erro | object | não | Só com |
erro.codigo | string | sim | |
erro.codigoExterno | string | null | sim | |
erro.mensagem | string | sim | |
custoEstimado | object | null | não | Só em templates: estimativa pela tabela pública. |
custoEstimado.categoria | enum | sim | Valores: |
custoEstimado.centavos | integer | sim | Estimativa do que a Meta cobra por esta mensagem, em centavos de BRL. |
custoEstimado.precoUsd | string | sim | |
custoEstimado.cambio | string | sim | |
custoEstimado.tabelaVersao | integer | sim | |
custoEstimado.estimado | enum | sim | Valores: |
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.
/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
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
para | string | não | Telefone do contato com DDI e DDD. Aceita máscara ( |
contatos | object[] | sim | No máximo 10 itens. |
contatos[].nome | string | sim | Tamanho: 1 a 256. |
contatos[].telefones | string[] | sim | No máximo 5 itens. |
contatos[].empresa | string | não | Tamanho: 0 a 256. |
contatos[].email | string | não | Tamanho: 0 a 256. |
respondendoA | string | não |
|
mencoes | string[] | não | Telefones mencionados (só em grupos; em conversa individual é aceito e ignorado). Só NAO_OFICIAL. No máximo 50 itens. |
grupoId | string | não | Envia ao grupo ( |
mencionarTodos | boolean | nã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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da mensagem ( |
tipoNumero | enum | sim | Valores: |
tipoEnvio | enum | sim | Valores: |
status | enum | sim | Valores: |
enfileirada | boolean | sim | true = na fila de cadência do número (não oficial); sai logo em seguida. |
custoEvitado | object | null | sim | Só números NAO_OFICIAL: quanto custaria a mesma mensagem na API Oficial. |
custoEvitado.marketingCentavos | integer | sim | |
custoEvitado.utilityCentavos | integer | sim | |
contatoOptout | boolean | sim | O contato pediu para sair; o envio não é bloqueado, só sinalizado. |
janelaAberta | boolean | sim | Janela de 24h aberta (informativo). |
erro | object | não | Só com |
erro.codigo | string | sim | |
erro.codigoExterno | string | null | sim | |
erro.mensagem | string | sim | |
custoEstimado | object | null | não | Só em templates: estimativa pela tabela pública. |
custoEstimado.categoria | enum | sim | Valores: |
custoEstimado.centavos | integer | sim | Estimativa do que a Meta cobra por esta mensagem, em centavos de BRL. |
custoEstimado.precoUsd | string | sim | |
custoEstimado.cambio | string | sim | |
custoEstimado.tabelaVersao | integer | sim | |
custoEstimado.estimado | enum | sim | Valores: |
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.
/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
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
para | string | não | Telefone do contato com DDI e DDD. Aceita máscara ( |
url | string | não | URL HTTPS pública de um WebP (até 500 KB; animada até 1 MB). Tamanho: 1 a 2048. |
base64 | string | não | Conteúdo em base64 (ou data URL). Alternativa a |
mimeType | string | não | Obrigatório com |
respondendoA | string | não |
|
mencoes | string[] | não | Telefones mencionados (só em grupos; em conversa individual é aceito e ignorado). Só NAO_OFICIAL. No máximo 50 itens. |
grupoId | string | não | Envia ao grupo ( |
mencionarTodos | boolean | nã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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da mensagem ( |
tipoNumero | enum | sim | Valores: |
tipoEnvio | enum | sim | Valores: |
status | enum | sim | Valores: |
enfileirada | boolean | sim | true = na fila de cadência do número (não oficial); sai logo em seguida. |
custoEvitado | object | null | sim | Só números NAO_OFICIAL: quanto custaria a mesma mensagem na API Oficial. |
custoEvitado.marketingCentavos | integer | sim | |
custoEvitado.utilityCentavos | integer | sim | |
contatoOptout | boolean | sim | O contato pediu para sair; o envio não é bloqueado, só sinalizado. |
janelaAberta | boolean | sim | Janela de 24h aberta (informativo). |
erro | object | não | Só com |
erro.codigo | string | sim | |
erro.codigoExterno | string | null | sim | |
erro.mensagem | string | sim | |
custoEstimado | object | null | não | Só em templates: estimativa pela tabela pública. |
custoEstimado.categoria | enum | sim | Valores: |
custoEstimado.centavos | integer | sim | Estimativa do que a Meta cobra por esta mensagem, em centavos de BRL. |
custoEstimado.precoUsd | string | sim | |
custoEstimado.cambio | string | sim | |
custoEstimado.tabelaVersao | integer | sim | |
custoEstimado.estimado | enum | sim | Valores: |
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.
/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
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Número OFICIAL. Tamanho: 1 a 64. |
para | string | sim | Telefone do contato com DDI e DDD. Aceita máscara ( |
tipo | enum | sim | Valores: |
corpo | string | sim | Tamanho: 1 a 1024. |
cabecalho | string | não | Tamanho: 0 a 60. |
rodape | string | não | Tamanho: 0 a 60. |
botoes | object[] | não | Só em |
botoes[].id | string | sim | Tamanho: 1 a 256. |
botoes[].titulo | string | sim | Tamanho: 1 a 20. |
textoBotao | string | não | Em |
secoes | object[] | não | Só em |
secoes[].titulo | string | não | Tamanho: 0 a 24. |
secoes[].itens | object[] | sim | |
secoes[].itens[].id | string | sim | Tamanho: 1 a 200. |
secoes[].itens[].titulo | string | sim | Tamanho: 1 a 24. |
secoes[].itens[].descricao | string | não | Tamanho: 0 a 72. |
url | string | não | Só em |
respondendoA | string | nã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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da mensagem ( |
tipoNumero | enum | sim | Valores: |
tipoEnvio | enum | sim | Valores: |
status | enum | sim | Valores: |
enfileirada | boolean | sim | true = na fila de cadência do número (não oficial); sai logo em seguida. |
custoEvitado | object | null | sim | Só números NAO_OFICIAL: quanto custaria a mesma mensagem na API Oficial. |
custoEvitado.marketingCentavos | integer | sim | |
custoEvitado.utilityCentavos | integer | sim | |
contatoOptout | boolean | sim | O contato pediu para sair; o envio não é bloqueado, só sinalizado. |
janelaAberta | boolean | sim | Janela de 24h aberta (informativo). |
erro | object | não | Só com |
erro.codigo | string | sim | |
erro.codigoExterno | string | null | sim | |
erro.mensagem | string | sim | |
custoEstimado | object | null | não | Só em templates: estimativa pela tabela pública. |
custoEstimado.categoria | enum | sim | Valores: |
custoEstimado.centavos | integer | sim | Estimativa do que a Meta cobra por esta mensagem, em centavos de BRL. |
custoEstimado.precoUsd | string | sim | |
custoEstimado.cambio | string | sim | |
custoEstimado.tabelaVersao | integer | sim | |
custoEstimado.estimado | enum | sim | Valores: |
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.
/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
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Número NAO_OFICIAL. Tamanho: 1 a 64. |
para | string | não | Telefone do contato com DDI e DDD. Aceita máscara ( |
pergunta | string | sim | Tamanho: 1 a 255. |
opcoes | string[] | sim | No máximo 12 itens. |
maxRespostas | integer | não | Quantas opções cada contato pode marcar. Intervalo: 1 a ∞. Padrão: |
respondendoA | string | não |
|
mencoes | string[] | não | Telefones mencionados (só em grupos; em conversa individual é aceito e ignorado). Só NAO_OFICIAL. No máximo 50 itens. |
grupoId | string | não | Envia ao grupo ( |
mencionarTodos | boolean | nã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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da mensagem ( |
tipoNumero | enum | sim | Valores: |
tipoEnvio | enum | sim | Valores: |
status | enum | sim | Valores: |
enfileirada | boolean | sim | true = na fila de cadência do número (não oficial); sai logo em seguida. |
custoEvitado | object | null | sim | Só números NAO_OFICIAL: quanto custaria a mesma mensagem na API Oficial. |
custoEvitado.marketingCentavos | integer | sim | |
custoEvitado.utilityCentavos | integer | sim | |
contatoOptout | boolean | sim | O contato pediu para sair; o envio não é bloqueado, só sinalizado. |
janelaAberta | boolean | sim | Janela de 24h aberta (informativo). |
erro | object | não | Só com |
erro.codigo | string | sim | |
erro.codigoExterno | string | null | sim | |
erro.mensagem | string | sim | |
custoEstimado | object | null | não | Só em templates: estimativa pela tabela pública. |
custoEstimado.categoria | enum | sim | Valores: |
custoEstimado.centavos | integer | sim | Estimativa do que a Meta cobra por esta mensagem, em centavos de BRL. |
custoEstimado.precoUsd | string | sim | |
custoEstimado.cambio | string | sim | |
custoEstimado.tabelaVersao | integer | sim | |
custoEstimado.estimado | enum | sim | Valores: |
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.
/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
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Número OFICIAL. Tamanho: 1 a 64. |
para | string | sim | Telefone do contato com DDI e DDD. Aceita máscara ( |
templateId | string | sim |
|
variaveis | string[] | não | Valores das variáveis |
Exemplo de corpo
{
"numeroId": "clx0numero000000000000001",
"para": "5511987654321",
"templateId": "clx0template0000000000001",
"variaveis": [
"Ana",
"COD-1042"
]
} Resposta 202 · Aceito
Mensagem aceita.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da mensagem ( |
tipoNumero | enum | sim | Valores: |
tipoEnvio | enum | sim | Valores: |
status | enum | sim | Valores: |
enfileirada | boolean | sim | true = na fila de cadência do número (não oficial); sai logo em seguida. |
custoEvitado | object | null | sim | Só números NAO_OFICIAL: quanto custaria a mesma mensagem na API Oficial. |
custoEvitado.marketingCentavos | integer | sim | |
custoEvitado.utilityCentavos | integer | sim | |
contatoOptout | boolean | sim | O contato pediu para sair; o envio não é bloqueado, só sinalizado. |
janelaAberta | boolean | sim | Janela de 24h aberta (informativo). |
erro | object | não | Só com |
erro.codigo | string | sim | |
erro.codigoExterno | string | null | sim | |
erro.mensagem | string | sim | |
custoEstimado | object | null | não | Só em templates: estimativa pela tabela pública. |
custoEstimado.categoria | enum | sim | Valores: |
custoEstimado.centavos | integer | sim | Estimativa do que a Meta cobra por esta mensagem, em centavos de BRL. |
custoEstimado.precoUsd | string | sim | |
custoEstimado.cambio | string | sim | |
custoEstimado.tabelaVersao | integer | sim | |
custoEstimado.estimado | enum | sim | Valores: |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da mensagem ( |
Resposta 200 · OK
A mensagem.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
numeroId | string | sim | |
tipoNumero | enum | sim | Valores: |
conversaId | string | sim | |
contato | object | sim | |
contato.telefone | string | sim | |
direcao | enum | sim | Valores: |
origem | enum | sim | Valores: |
tipoEnvio | enum | null | sim | Valores: |
tipoConteudo | enum | sim | Valores: |
texto | string | null | sim | Nulo depois da retenção ( |
midia | object | null | sim | |
midia.tipo | enum | sim | Valores: |
midia.mimeType | string | null | sim | |
midia.disponivel | boolean | sim | |
status | enum | sim | Valores: |
enfileirada | boolean | sim | |
contatoOptout | boolean | sim | |
custoEvitado | object | null | sim | |
custoEvitado.marketingCentavos | integer | sim | |
custoEvitado.utilityCentavos | integer | sim | |
erro | object | null | sim | |
erro.codigo | string | null | sim | |
erro.mensagem | string | null | sim | |
criadoEm | string | sim | |
enviadoEm | string | null | sim | |
entregueEm | string | null | sim | |
lidoEm | string | null | sim | |
removida | boolean | sim | Conteúdo removido pela retenção de 90 dias; os metadados permanecem. |
editadaEm | string | null | sim | O autor editou a mensagem (o texto já é o novo). |
apagadaEm | string | null | sim | O autor apagou para todos: texto e mídia foram limpos. |
reacoes | object[] | sim | Reação atual de cada lado. |
reacoes[].autor | enum | sim | Valores: |
reacoes[].emoji | string | sim | |
contatos | object[] | null | sim | Só em |
contatos[].nome | string | sim | |
contatos[].telefones | string[] | sim | |
contatos[].empresa | string | não | |
contatos[].email | string | não | |
enquete | object | null | sim | Só em enquetes enviadas pelo número; o detalhe por contato está em |
enquete.maxRespostas | integer | sim | |
enquete.opcoes | object[] | sim | |
enquete.opcoes[].opcao | string | sim | |
enquete.opcoes[].total | integer | sim | |
respondendoA | object | null | sim | Mensagem citada. |
respondendoA.id | string | sim | |
respondendoA.trecho | string | null | sim | |
remetente | object | null | sim | Só em grupo: quem escreveu. |
remetente.telefone | string | null | sim | |
remetente.nome | string | null | sim | |
interativo | object | null | sim | Mensagem interativa enviada (oficial): o tipo e os títulos das opções. |
interativo.tipo | enum | sim | Valores: |
interativo.opcoes | string[] | sim | |
interacao | object | null | sim | Resposta do contato a uma interativa: o botão ou item escolhido. |
interacao.tipo | enum | sim | Valores: |
interacao.id | string | sim | |
interacao.titulo | string | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da mensagem. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
texto | string | sim | Tamanho: 1 a 4096. |
Exemplo de corpo
{
"texto": "Corrigindo: entrega no sábado."
} Resposta 200 · OK
A mensagem editada.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
numeroId | string | sim | |
tipoNumero | enum | sim | Valores: |
conversaId | string | sim | |
contato | object | sim | |
contato.telefone | string | sim | |
direcao | enum | sim | Valores: |
origem | enum | sim | Valores: |
tipoEnvio | enum | null | sim | Valores: |
tipoConteudo | enum | sim | Valores: |
texto | string | null | sim | Nulo depois da retenção ( |
midia | object | null | sim | |
midia.tipo | enum | sim | Valores: |
midia.mimeType | string | null | sim | |
midia.disponivel | boolean | sim | |
status | enum | sim | Valores: |
enfileirada | boolean | sim | |
contatoOptout | boolean | sim | |
custoEvitado | object | null | sim | |
custoEvitado.marketingCentavos | integer | sim | |
custoEvitado.utilityCentavos | integer | sim | |
erro | object | null | sim | |
erro.codigo | string | null | sim | |
erro.mensagem | string | null | sim | |
criadoEm | string | sim | |
enviadoEm | string | null | sim | |
entregueEm | string | null | sim | |
lidoEm | string | null | sim | |
removida | boolean | sim | Conteúdo removido pela retenção de 90 dias; os metadados permanecem. |
editadaEm | string | null | sim | O autor editou a mensagem (o texto já é o novo). |
apagadaEm | string | null | sim | O autor apagou para todos: texto e mídia foram limpos. |
reacoes | object[] | sim | Reação atual de cada lado. |
reacoes[].autor | enum | sim | Valores: |
reacoes[].emoji | string | sim | |
contatos | object[] | null | sim | Só em |
contatos[].nome | string | sim | |
contatos[].telefones | string[] | sim | |
contatos[].empresa | string | não | |
contatos[].email | string | não | |
enquete | object | null | sim | Só em enquetes enviadas pelo número; o detalhe por contato está em |
enquete.maxRespostas | integer | sim | |
enquete.opcoes | object[] | sim | |
enquete.opcoes[].opcao | string | sim | |
enquete.opcoes[].total | integer | sim | |
respondendoA | object | null | sim | Mensagem citada. |
respondendoA.id | string | sim | |
respondendoA.trecho | string | null | sim | |
remetente | object | null | sim | Só em grupo: quem escreveu. |
remetente.telefone | string | null | sim | |
remetente.nome | string | null | sim | |
interativo | object | null | sim | Mensagem interativa enviada (oficial): o tipo e os títulos das opções. |
interativo.tipo | enum | sim | Valores: |
interativo.opcoes | string[] | sim | |
interacao | object | null | sim | Resposta do contato a uma interativa: o botão ou item escolhido. |
interacao.tipo | enum | sim | Valores: |
interacao.id | string | sim | |
interacao.titulo | string | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da mensagem. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Resposta 200 · OK
A mensagem apagada.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
numeroId | string | sim | |
tipoNumero | enum | sim | Valores: |
conversaId | string | sim | |
contato | object | sim | |
contato.telefone | string | sim | |
direcao | enum | sim | Valores: |
origem | enum | sim | Valores: |
tipoEnvio | enum | null | sim | Valores: |
tipoConteudo | enum | sim | Valores: |
texto | string | null | sim | Nulo depois da retenção ( |
midia | object | null | sim | |
midia.tipo | enum | sim | Valores: |
midia.mimeType | string | null | sim | |
midia.disponivel | boolean | sim | |
status | enum | sim | Valores: |
enfileirada | boolean | sim | |
contatoOptout | boolean | sim | |
custoEvitado | object | null | sim | |
custoEvitado.marketingCentavos | integer | sim | |
custoEvitado.utilityCentavos | integer | sim | |
erro | object | null | sim | |
erro.codigo | string | null | sim | |
erro.mensagem | string | null | sim | |
criadoEm | string | sim | |
enviadoEm | string | null | sim | |
entregueEm | string | null | sim | |
lidoEm | string | null | sim | |
removida | boolean | sim | Conteúdo removido pela retenção de 90 dias; os metadados permanecem. |
editadaEm | string | null | sim | O autor editou a mensagem (o texto já é o novo). |
apagadaEm | string | null | sim | O autor apagou para todos: texto e mídia foram limpos. |
reacoes | object[] | sim | Reação atual de cada lado. |
reacoes[].autor | enum | sim | Valores: |
reacoes[].emoji | string | sim | |
contatos | object[] | null | sim | Só em |
contatos[].nome | string | sim | |
contatos[].telefones | string[] | sim | |
contatos[].empresa | string | não | |
contatos[].email | string | não | |
enquete | object | null | sim | Só em enquetes enviadas pelo número; o detalhe por contato está em |
enquete.maxRespostas | integer | sim | |
enquete.opcoes | object[] | sim | |
enquete.opcoes[].opcao | string | sim | |
enquete.opcoes[].total | integer | sim | |
respondendoA | object | null | sim | Mensagem citada. |
respondendoA.id | string | sim | |
respondendoA.trecho | string | null | sim | |
remetente | object | null | sim | Só em grupo: quem escreveu. |
remetente.telefone | string | null | sim | |
remetente.nome | string | null | sim | |
interativo | object | null | sim | Mensagem interativa enviada (oficial): o tipo e os títulos das opções. |
interativo.tipo | enum | sim | Valores: |
interativo.opcoes | string[] | sim | |
interacao | object | null | sim | Resposta do contato a uma interativa: o botão ou item escolhido. |
interacao.tipo | enum | sim | Valores: |
interacao.id | string | sim | |
interacao.titulo | string | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da mensagem. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
emoji | string | sim | 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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
numeroId | string | sim | |
tipoNumero | enum | sim | Valores: |
conversaId | string | sim | |
contato | object | sim | |
contato.telefone | string | sim | |
direcao | enum | sim | Valores: |
origem | enum | sim | Valores: |
tipoEnvio | enum | null | sim | Valores: |
tipoConteudo | enum | sim | Valores: |
texto | string | null | sim | Nulo depois da retenção ( |
midia | object | null | sim | |
midia.tipo | enum | sim | Valores: |
midia.mimeType | string | null | sim | |
midia.disponivel | boolean | sim | |
status | enum | sim | Valores: |
enfileirada | boolean | sim | |
contatoOptout | boolean | sim | |
custoEvitado | object | null | sim | |
custoEvitado.marketingCentavos | integer | sim | |
custoEvitado.utilityCentavos | integer | sim | |
erro | object | null | sim | |
erro.codigo | string | null | sim | |
erro.mensagem | string | null | sim | |
criadoEm | string | sim | |
enviadoEm | string | null | sim | |
entregueEm | string | null | sim | |
lidoEm | string | null | sim | |
removida | boolean | sim | Conteúdo removido pela retenção de 90 dias; os metadados permanecem. |
editadaEm | string | null | sim | O autor editou a mensagem (o texto já é o novo). |
apagadaEm | string | null | sim | O autor apagou para todos: texto e mídia foram limpos. |
reacoes | object[] | sim | Reação atual de cada lado. |
reacoes[].autor | enum | sim | Valores: |
reacoes[].emoji | string | sim | |
contatos | object[] | null | sim | Só em |
contatos[].nome | string | sim | |
contatos[].telefones | string[] | sim | |
contatos[].empresa | string | não | |
contatos[].email | string | não | |
enquete | object | null | sim | Só em enquetes enviadas pelo número; o detalhe por contato está em |
enquete.maxRespostas | integer | sim | |
enquete.opcoes | object[] | sim | |
enquete.opcoes[].opcao | string | sim | |
enquete.opcoes[].total | integer | sim | |
respondendoA | object | null | sim | Mensagem citada. |
respondendoA.id | string | sim | |
respondendoA.trecho | string | null | sim | |
remetente | object | null | sim | Só em grupo: quem escreveu. |
remetente.telefone | string | null | sim | |
remetente.nome | string | null | sim | |
interativo | object | null | sim | Mensagem interativa enviada (oficial): o tipo e os títulos das opções. |
interativo.tipo | enum | sim | Valores: |
interativo.opcoes | string[] | sim | |
interacao | object | null | sim | Resposta do contato a uma interativa: o botão ou item escolhido. |
interacao.tipo | enum | sim | Valores: |
interacao.id | string | sim | |
interacao.titulo | string | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da enquete. |
Resposta 200 · OK
Os votos.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
mensagemId | string | sim | |
pergunta | string | null | sim | |
maxRespostas | integer | sim | |
opcoes | object[] | sim | Na ordem da enquete, com o total de votos atuais. |
opcoes[].opcao | string | sim | |
opcoes[].total | integer | sim | |
opcoes[].telefones | string[] | sim | |
votos | object[] | sim | Voto atual de cada contato (trocar o voto substitui o anterior). |
votos[].telefone | string | sim | |
votos[].opcoes | string[] | sim | |
votos[].votadoEm | string | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da mensagem recebida. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Resposta 200 · OK
A mensagem.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
numeroId | string | sim | |
tipoNumero | enum | sim | Valores: |
conversaId | string | sim | |
contato | object | sim | |
contato.telefone | string | sim | |
direcao | enum | sim | Valores: |
origem | enum | sim | Valores: |
tipoEnvio | enum | null | sim | Valores: |
tipoConteudo | enum | sim | Valores: |
texto | string | null | sim | Nulo depois da retenção ( |
midia | object | null | sim | |
midia.tipo | enum | sim | Valores: |
midia.mimeType | string | null | sim | |
midia.disponivel | boolean | sim | |
status | enum | sim | Valores: |
enfileirada | boolean | sim | |
contatoOptout | boolean | sim | |
custoEvitado | object | null | sim | |
custoEvitado.marketingCentavos | integer | sim | |
custoEvitado.utilityCentavos | integer | sim | |
erro | object | null | sim | |
erro.codigo | string | null | sim | |
erro.mensagem | string | null | sim | |
criadoEm | string | sim | |
enviadoEm | string | null | sim | |
entregueEm | string | null | sim | |
lidoEm | string | null | sim | |
removida | boolean | sim | Conteúdo removido pela retenção de 90 dias; os metadados permanecem. |
editadaEm | string | null | sim | O autor editou a mensagem (o texto já é o novo). |
apagadaEm | string | null | sim | O autor apagou para todos: texto e mídia foram limpos. |
reacoes | object[] | sim | Reação atual de cada lado. |
reacoes[].autor | enum | sim | Valores: |
reacoes[].emoji | string | sim | |
contatos | object[] | null | sim | Só em |
contatos[].nome | string | sim | |
contatos[].telefones | string[] | sim | |
contatos[].empresa | string | não | |
contatos[].email | string | não | |
enquete | object | null | sim | Só em enquetes enviadas pelo número; o detalhe por contato está em |
enquete.maxRespostas | integer | sim | |
enquete.opcoes | object[] | sim | |
enquete.opcoes[].opcao | string | sim | |
enquete.opcoes[].total | integer | sim | |
respondendoA | object | null | sim | Mensagem citada. |
respondendoA.id | string | sim | |
respondendoA.trecho | string | null | sim | |
remetente | object | null | sim | Só em grupo: quem escreveu. |
remetente.telefone | string | null | sim | |
remetente.nome | string | null | sim | |
interativo | object | null | sim | Mensagem interativa enviada (oficial): o tipo e os títulos das opções. |
interativo.tipo | enum | sim | Valores: |
interativo.opcoes | string[] | sim | |
interacao | object | null | sim | Resposta do contato a uma interativa: o botão ou item escolhido. |
interacao.tipo | enum | sim | Valores: |
interacao.id | string | sim | |
interacao.titulo | string | sim |
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.
/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
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
para | string | sim | Telefone do contato com DDI e DDD. Aceita máscara ( |
estado | enum | sim | Valores: |
duracaoMs | integer | nã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: |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
telefone | string | sim | Telefone do contato com DDI e DDD. Aceita máscara ( |
cursor | string | não |
|
limite | integer | não |
Resposta 200 · OK
A conversa e a página de mensagens.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
conversa | object | sim | |
conversa.id | string | sim | |
conversa.numeroId | string | sim | |
conversa.tipoNumero | enum | sim | Valores: |
conversa.contato | object | sim | |
conversa.contato.telefone | string | sim | |
conversa.contato.nome | string | null | sim | |
conversa.ultimaMensagemEm | string | sim | |
conversa.ultimaEntradaEm | string | null | sim | |
conversa.janela24h | object | sim | |
conversa.janela24h.aberta | boolean | sim | |
conversa.janela24h.expiraEm | string | null | sim | |
conversa.tipo | enum | sim | Em GRUPO, |
conversa.etiquetas | object[] | sim | Etiquetas do WhatsApp Business aplicadas (não oficial). |
conversa.etiquetas[].id | string | sim | |
conversa.etiquetas[].nome | string | sim | |
conversa.etiquetas[].cor | integer | sim | |
conversa.arquivada | boolean | sim | |
conversa.fixada | boolean | sim | |
conversa.silenciadaAte | string | null | sim | Fim do silêncio; muito no futuro = sempre. |
mensagens | object | sim | |
mensagens.itens | object[] | sim | Da mais nova para a mais antiga. |
mensagens.itens[].id | string | sim | |
mensagens.itens[].numeroId | string | sim | |
mensagens.itens[].tipoNumero | enum | sim | Valores: |
mensagens.itens[].conversaId | string | sim | |
mensagens.itens[].contato | object | sim | |
mensagens.itens[].contato.telefone | string | sim | |
mensagens.itens[].direcao | enum | sim | Valores: |
mensagens.itens[].origem | enum | sim | Valores: |
mensagens.itens[].tipoEnvio | enum | null | sim | Valores: |
mensagens.itens[].tipoConteudo | enum | sim | Valores: |
mensagens.itens[].texto | string | null | sim | Nulo depois da retenção ( |
mensagens.itens[].midia | object | null | sim | |
mensagens.itens[].midia.tipo | enum | sim | Valores: |
mensagens.itens[].midia.mimeType | string | null | sim | |
mensagens.itens[].midia.disponivel | boolean | sim | |
mensagens.itens[].status | enum | sim | Valores: |
mensagens.itens[].enfileirada | boolean | sim | |
mensagens.itens[].contatoOptout | boolean | sim | |
mensagens.itens[].custoEvitado | object | null | sim | |
mensagens.itens[].custoEvitado.marketingCentavos | integer | sim | |
mensagens.itens[].custoEvitado.utilityCentavos | integer | sim | |
mensagens.itens[].erro | object | null | sim | |
mensagens.itens[].erro.codigo | string | null | sim | |
mensagens.itens[].erro.mensagem | string | null | sim | |
mensagens.itens[].criadoEm | string | sim | |
mensagens.itens[].enviadoEm | string | null | sim | |
mensagens.itens[].entregueEm | string | null | sim | |
mensagens.itens[].lidoEm | string | null | sim | |
mensagens.itens[].removida | boolean | sim | Conteúdo removido pela retenção de 90 dias; os metadados permanecem. |
mensagens.itens[].editadaEm | string | null | sim | O autor editou a mensagem (o texto já é o novo). |
mensagens.itens[].apagadaEm | string | null | sim | O autor apagou para todos: texto e mídia foram limpos. |
mensagens.itens[].reacoes | object[] | sim | Reação atual de cada lado. |
mensagens.itens[].reacoes[].autor | enum | sim | Valores: |
mensagens.itens[].reacoes[].emoji | string | sim | |
mensagens.itens[].contatos | object[] | null | sim | Só em |
mensagens.itens[].contatos[].nome | string | sim | |
mensagens.itens[].contatos[].telefones | string[] | sim | |
mensagens.itens[].contatos[].empresa | string | não | |
mensagens.itens[].contatos[].email | string | não | |
mensagens.itens[].enquete | object | null | sim | Só em enquetes enviadas pelo número; o detalhe por contato está em |
mensagens.itens[].enquete.maxRespostas | integer | sim | |
mensagens.itens[].enquete.opcoes | object[] | sim | |
mensagens.itens[].enquete.opcoes[].opcao | string | sim | |
mensagens.itens[].enquete.opcoes[].total | integer | sim | |
mensagens.itens[].respondendoA | object | null | sim | Mensagem citada. |
mensagens.itens[].respondendoA.id | string | sim | |
mensagens.itens[].respondendoA.trecho | string | null | sim | |
mensagens.itens[].remetente | object | null | sim | Só em grupo: quem escreveu. |
mensagens.itens[].remetente.telefone | string | null | sim | |
mensagens.itens[].remetente.nome | string | null | sim | |
mensagens.itens[].interativo | object | null | sim | Mensagem interativa enviada (oficial): o tipo e os títulos das opções. |
mensagens.itens[].interativo.tipo | enum | sim | Valores: |
mensagens.itens[].interativo.opcoes | string[] | sim | |
mensagens.itens[].interacao | object | null | sim | Resposta do contato a uma interativa: o botão ou item escolhido. |
mensagens.itens[].interacao.tipo | enum | sim | Valores: |
mensagens.itens[].interacao.id | string | sim | |
mensagens.itens[].interacao.titulo | string | sim | |
mensagens.proximoCursor | string | null | sim |
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 - get
/v1/contatos/{telefone} - get
/v1/contatos/{telefone}/foto - post
/v1/contatos/{telefone}/bloquear - post
/v1/contatos/{telefone}/desbloquear - get
/v1/numeros/{id}/bloqueados - get
/v1/numeros/{id}/contatos - patch
/v1/numeros/{id}/perfil - get
/v1/numeros/{id}/privacidade - put
/v1/numeros/{id}/privacidade - get
/v1/numeros/{id}/configuracoes - put
/v1/numeros/{id}/configuracoes - post
/v1/numeros/{id}/historico/sincronizar - post
/v1/conversas/{id}/arquivar - post
/v1/conversas/{id}/desarquivar - post
/v1/conversas/{id}/fixar - post
/v1/conversas/{id}/desafixar - post
/v1/conversas/{id}/silenciar - post
/v1/conversas/{id}/dessilenciar - post
/v1/chamadas/{chamadaId}/rejeitar - post
/v1/numeros/{id}/status - get
/v1/numeros/{id}/etiquetas - post
/v1/numeros/{id}/etiquetas - delete
/v1/numeros/{id}/etiquetas/{etiquetaId} - post
/v1/conversas/{id}/etiquetas - post
/v1/mensagens/{id}/etiquetas
/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)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Número NAO_OFICIAL conectado. Tamanho: 1 a 64. |
telefones | string[] | sim | No máximo 100 itens. |
Exemplo de corpo
{
"numeroId": "clx0numero000000000000001",
"telefones": [
"5511987654321",
"27998765432"
]
} Resposta 200 · OK
Resultado por telefone.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
resultados | object[] | sim | |
resultados[].telefone | string | sim | Como foi enviado. |
resultados[].temWhatsapp | boolean | sim | |
resultados[].telefoneNormalizado | string | null | sim | Como o WhatsApp conhece o número (ex.: com ou sem o nono dígito). |
resultados[].contaComercial | boolean | sim | |
restanteNaHora | integer | sim | 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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
telefone | string | sim | Telefone do contato (E.164, com ou sem máscara). |
Parâmetros de consulta (query string)
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
Resposta 200 · OK
O contato.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
telefone | string | sim | |
temWhatsapp | boolean | sim | |
nome | string | null | sim | Nome comercial verificado, quando houver. |
recado | string | null | sim | |
fotoUrl | string | null | sim | Link temporário do WhatsApp; nulo se a privacidade do contato esconde a foto. |
fotoExpiraEm | string | null | sim | |
contaComercial | boolean | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
telefone | string | sim | Telefone do contato (E.164, com ou sem máscara). |
Parâmetros de consulta (query string)
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
telefone | string | sim | Telefone do contato (E.164, com ou sem máscara). |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
telefone | string | sim | Telefone do contato (E.164, com ou sem máscara). |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do número. |
Resposta 200 · OK
Telefones bloqueados.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
telefones | string[] | 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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do número. |
Parâmetros de consulta (query string)
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
cursor | integer | null | não | Posição (o |
limite | integer | não |
Resposta 200 · OK
Uma página da agenda.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
itens | object[] | sim | |
itens[].telefone | string | sim | |
itens[].nome | string | null | sim | Nome salvo na agenda do celular. |
itens[].nomePublico | string | null | sim | |
itens[].nomeComercial | string | null | sim | |
total | integer | sim | |
proximoCursor | integer | null | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do número. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
nome | string | não | Tamanho: 1 a 25. |
recado | string | não | Tamanho: 0 a 139. |
foto | string | nã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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do número. |
Resposta 200 · OK
Privacidade atual.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
vistoPorUltimo | enum | sim | Valores: |
foto | enum | sim | Valores: |
recado | enum | sim | Valores: |
adicionarGrupos | enum | sim | Valores: |
confirmacaoLeitura | enum | sim | Valores: |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do número. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
vistoPorUltimo | enum | não | Valores: |
foto | enum | não | Valores: |
recado | enum | não | Valores: |
adicionarGrupos | enum | não | Valores: |
confirmacaoLeitura | enum | não | Valores: |
Exemplo de corpo
{
"vistoPorUltimo": "contatos"
} Resposta 200 · OK
Privacidade depois da alteração.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
vistoPorUltimo | enum | sim | Valores: |
foto | enum | sim | Valores: |
recado | enum | sim | Valores: |
adicionarGrupos | enum | sim | Valores: |
confirmacaoLeitura | enum | sim | Valores: |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do número. |
Resposta 200 · OK
As configurações.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
rejeitarChamadas | boolean | sim | |
mensagemRejeicao | string | null | sim | Tamanho: 0 a 500. |
sempreOnline | boolean | sim | Aumenta o risco de banimento: comportamento não humano. |
lerAutomaticamente | boolean | sim | Aumenta o risco de banimento: comportamento não humano. |
ignorarGrupos | boolean | sim | |
ignorarStatus | boolean | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do número. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
rejeitarChamadas | boolean | não | |
mensagemRejeicao | string | null | não | Tamanho: 0 a 500. |
sempreOnline | boolean | não | Aumenta o risco de banimento: comportamento não humano. |
lerAutomaticamente | boolean | não | Aumenta o risco de banimento: comportamento não humano. |
ignorarGrupos | boolean | não | |
ignorarStatus | boolean | não |
Exemplo de corpo
{
"rejeitarChamadas": true,
"mensagemRejeicao": "Não atendemos ligações, mande mensagem"
} Resposta 200 · OK
As configurações aplicadas.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
rejeitarChamadas | boolean | sim | |
mensagemRejeicao | string | null | sim | Tamanho: 0 a 500. |
sempreOnline | boolean | sim | Aumenta o risco de banimento: comportamento não humano. |
lerAutomaticamente | boolean | sim | Aumenta o risco de banimento: comportamento não humano. |
ignorarGrupos | boolean | sim | |
ignorarStatus | boolean | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do número. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
telefone | string | sim | Conversa a sincronizar. Tamanho: 8 a 30. |
quantidade | integer | não | Intervalo: 1 a 100. Padrão: |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da conversa. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Resposta 200 · OK
A conversa.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
numeroId | string | sim | |
tipoNumero | enum | sim | Valores: |
contato | object | sim | |
contato.telefone | string | sim | |
contato.nome | string | null | sim | |
ultimaMensagemEm | string | sim | |
ultimaEntradaEm | string | null | sim | |
janela24h | object | sim | |
janela24h.aberta | boolean | sim | |
janela24h.expiraEm | string | null | sim | |
tipo | enum | sim | Em GRUPO, |
etiquetas | object[] | sim | Etiquetas do WhatsApp Business aplicadas (não oficial). |
etiquetas[].id | string | sim | |
etiquetas[].nome | string | sim | |
etiquetas[].cor | integer | sim | |
arquivada | boolean | sim | |
fixada | boolean | sim | |
silenciadaAte | string | null | sim | 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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da conversa. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Resposta 200 · OK
A conversa.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
numeroId | string | sim | |
tipoNumero | enum | sim | Valores: |
contato | object | sim | |
contato.telefone | string | sim | |
contato.nome | string | null | sim | |
ultimaMensagemEm | string | sim | |
ultimaEntradaEm | string | null | sim | |
janela24h | object | sim | |
janela24h.aberta | boolean | sim | |
janela24h.expiraEm | string | null | sim | |
tipo | enum | sim | Em GRUPO, |
etiquetas | object[] | sim | Etiquetas do WhatsApp Business aplicadas (não oficial). |
etiquetas[].id | string | sim | |
etiquetas[].nome | string | sim | |
etiquetas[].cor | integer | sim | |
arquivada | boolean | sim | |
fixada | boolean | sim | |
silenciadaAte | string | null | sim | 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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da conversa. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Resposta 200 · OK
A conversa.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
numeroId | string | sim | |
tipoNumero | enum | sim | Valores: |
contato | object | sim | |
contato.telefone | string | sim | |
contato.nome | string | null | sim | |
ultimaMensagemEm | string | sim | |
ultimaEntradaEm | string | null | sim | |
janela24h | object | sim | |
janela24h.aberta | boolean | sim | |
janela24h.expiraEm | string | null | sim | |
tipo | enum | sim | Em GRUPO, |
etiquetas | object[] | sim | Etiquetas do WhatsApp Business aplicadas (não oficial). |
etiquetas[].id | string | sim | |
etiquetas[].nome | string | sim | |
etiquetas[].cor | integer | sim | |
arquivada | boolean | sim | |
fixada | boolean | sim | |
silenciadaAte | string | null | sim | 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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da conversa. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Resposta 200 · OK
A conversa.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
numeroId | string | sim | |
tipoNumero | enum | sim | Valores: |
contato | object | sim | |
contato.telefone | string | sim | |
contato.nome | string | null | sim | |
ultimaMensagemEm | string | sim | |
ultimaEntradaEm | string | null | sim | |
janela24h | object | sim | |
janela24h.aberta | boolean | sim | |
janela24h.expiraEm | string | null | sim | |
tipo | enum | sim | Em GRUPO, |
etiquetas | object[] | sim | Etiquetas do WhatsApp Business aplicadas (não oficial). |
etiquetas[].id | string | sim | |
etiquetas[].nome | string | sim | |
etiquetas[].cor | integer | sim | |
arquivada | boolean | sim | |
fixada | boolean | sim | |
silenciadaAte | string | null | sim | 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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da conversa. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
duracao | enum | sim | Valores: |
Exemplo de corpo
{
"duracao": "8h"
} Resposta 200 · OK
A conversa.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
numeroId | string | sim | |
tipoNumero | enum | sim | Valores: |
contato | object | sim | |
contato.telefone | string | sim | |
contato.nome | string | null | sim | |
ultimaMensagemEm | string | sim | |
ultimaEntradaEm | string | null | sim | |
janela24h | object | sim | |
janela24h.aberta | boolean | sim | |
janela24h.expiraEm | string | null | sim | |
tipo | enum | sim | Em GRUPO, |
etiquetas | object[] | sim | Etiquetas do WhatsApp Business aplicadas (não oficial). |
etiquetas[].id | string | sim | |
etiquetas[].nome | string | sim | |
etiquetas[].cor | integer | sim | |
arquivada | boolean | sim | |
fixada | boolean | sim | |
silenciadaAte | string | null | sim | 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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da conversa. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Resposta 200 · OK
A conversa.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
numeroId | string | sim | |
tipoNumero | enum | sim | Valores: |
contato | object | sim | |
contato.telefone | string | sim | |
contato.nome | string | null | sim | |
ultimaMensagemEm | string | sim | |
ultimaEntradaEm | string | null | sim | |
janela24h | object | sim | |
janela24h.aberta | boolean | sim | |
janela24h.expiraEm | string | null | sim | |
tipo | enum | sim | Em GRUPO, |
etiquetas | object[] | sim | Etiquetas do WhatsApp Business aplicadas (não oficial). |
etiquetas[].id | string | sim | |
etiquetas[].nome | string | sim | |
etiquetas[].cor | integer | sim | |
arquivada | boolean | sim | |
fixada | boolean | sim | |
silenciadaAte | string | null | sim | 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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
chamadaId | string | sim |
|
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do número. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
tipo | enum | sim | Valores: |
texto | string | não | Só em |
midia | object | não | Só em |
midia.tipo | enum | sim | Valores: |
midia.url | string | não | URL HTTPS pública. Tamanho: 0 a 2048. |
midia.base64 | string | não | Tamanho: 0 a 23000000. |
midia.mimeType | string | não | Tamanho: 0 a 100. |
legenda | string | nã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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
restanteHoje | integer | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do número. |
Resposta 200 · OK
As etiquetas.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
etiquetas | object[] | sim | |
etiquetas[].id | string | sim | |
etiquetas[].nome | string | sim | |
etiquetas[].cor | integer | sim | Í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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do número. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
etiquetaId | string | não | Para editar uma existente; sem ele, cria. Tamanho: 1 a 64. |
nome | string | sim | Tamanho: 1 a 100. |
cor | integer | não | Intervalo: 0 a 19. Padrão: |
Exemplo de corpo
{
"nome": "Pagamento pendente",
"cor": 3
} Resposta 200 · OK
A etiqueta.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
nome | string | sim | |
cor | integer | sim | Í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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do número. |
etiquetaId | string | sim | Identificador da etiqueta. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da conversa. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
etiquetaId | string | sim | Tamanho: 1 a 64. |
acao | enum | sim | Valores: |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da mensagem. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
etiquetaId | string | sim | Tamanho: 1 a 64. |
acao | enum | sim | Valores: |
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 - post
/v1/grupos - post
/v1/grupos/entrar - get
/v1/grupos/{id} - patch
/v1/grupos/{id} - post
/v1/grupos/{id}/participantes - get
/v1/grupos/{id}/convite - post
/v1/grupos/{id}/convite/revogar - delete
/v1/grupos/{id}/participacao
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do número. |
Resposta 200 · OK
Os grupos.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
grupos | object[] | sim | |
grupos[].id | string | sim | |
grupos[].numeroId | string | sim | |
grupos[].nome | string | null | sim | |
grupos[].descricao | string | null | sim | |
grupos[].soAdminsEnviam | boolean | sim | |
grupos[].soAdminsEditam | boolean | sim | |
grupos[].numeroEhAdmin | boolean | sim | |
grupos[].participantes | object[] | não | Só em |
grupos[].participantes[].telefone | string | null | sim | Nulo quando o WhatsApp só informa o identificador privado. |
grupos[].participantes[].admin | boolean | sim | |
grupos[].participantes[].superAdmin | boolean | sim | |
grupos[].totalParticipantes | integer | nã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.
/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
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Número NAO_OFICIAL conectado. Tamanho: 1 a 64. |
nome | string | sim | Tamanho: 1 a 100. |
participantes | string[] | sim | No máximo 50 itens. |
Exemplo de corpo
{
"numeroId": "clx0numero000000000000001",
"nome": "Clientes VIP",
"participantes": [
"5511987654321"
]
} Resposta 201 · Criado
O grupo criado.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
numeroId | string | sim | |
nome | string | null | sim | |
descricao | string | null | sim | |
soAdminsEnviam | boolean | sim | |
soAdminsEditam | boolean | sim | |
numeroEhAdmin | boolean | sim | |
participantes | object[] | não | Só em |
participantes[].telefone | string | null | sim | Nulo quando o WhatsApp só informa o identificador privado. |
participantes[].admin | boolean | sim | |
participantes[].superAdmin | boolean | sim | |
totalParticipantes | integer | nã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.
/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
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
link | string | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do grupo ( |
Resposta 200 · OK
O grupo.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
numeroId | string | sim | |
nome | string | null | sim | |
descricao | string | null | sim | |
soAdminsEnviam | boolean | sim | |
soAdminsEditam | boolean | sim | |
numeroEhAdmin | boolean | sim | |
participantes | object[] | não | Só em |
participantes[].telefone | string | null | sim | Nulo quando o WhatsApp só informa o identificador privado. |
participantes[].admin | boolean | sim | |
participantes[].superAdmin | boolean | sim | |
totalParticipantes | integer | nã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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do grupo ( |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
nome | string | não | Tamanho: 1 a 100. |
descricao | string | não | Tamanho: 0 a 2048. |
foto | string | não | JPEG ou PNG: URL HTTPS pública ou base64. Tamanho: 0 a 7000000. |
soAdminsEnviam | boolean | não | |
soAdminsEditam | boolean | não |
Exemplo de corpo
{
"descricao": "Ofertas da semana",
"soAdminsEnviam": true
} Resposta 200 · OK
O grupo.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
numeroId | string | sim | |
nome | string | null | sim | |
descricao | string | null | sim | |
soAdminsEnviam | boolean | sim | |
soAdminsEditam | boolean | sim | |
numeroEhAdmin | boolean | sim | |
participantes | object[] | não | Só em |
participantes[].telefone | string | null | sim | Nulo quando o WhatsApp só informa o identificador privado. |
participantes[].admin | boolean | sim | |
participantes[].superAdmin | boolean | sim | |
totalParticipantes | integer | nã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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do grupo ( |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
acao | enum | sim | Valores: |
telefones | string[] | 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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do grupo ( |
Resposta 200 · OK
O link.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
link | string | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do grupo ( |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Resposta 200 · OK
O link novo.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
link | string | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do grupo ( |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
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 - post
/v1/comunidades/{comunidadeId}/grupos - get
/v1/numeros/{id}/canais - post
/v1/numeros/{id}/canais - post
/v1/canais/convite - get
/v1/canais/{canalId} - get
/v1/canais/{canalId}/mensagens
/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
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
nome | string | sim | Tamanho: 1 a 100. |
Exemplo de corpo
{
"numeroId": "clx0numero000000000000001",
"nome": "Moradores do bairro"
} Resposta 201 · Criado
A comunidade criada.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | JID da comunidade no WhatsApp ( |
nome | string | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
comunidadeId | string | sim |
|
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
acao | enum | sim | Valores: |
grupoIds | string[] | sim |
|
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do número. |
Resposta 200 · OK
Os canais.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
canais | object[] | sim | |
canais[].id | string | sim | JID do canal ( |
canais[].nome | string | null | sim | |
canais[].descricao | string | null | sim | |
canais[].seguidores | integer | null | sim | |
canais[].convite | string | null | sim | Link de convite do canal. |
canais[].papel | string | null | sim | Papel do número no canal ( |
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.
/v1/numeros/{id}/canais Cria um canal
Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel).
Parâmetros de caminho
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do número. |
Headers
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
nome | string | sim | Tamanho: 1 a 100. |
descricao | string | não | Tamanho: 0 a 2048. |
Exemplo de corpo
{
"nome": "Ofertas da Loja",
"descricao": "Novidades toda semana"
} Resposta 201 · Criado
O canal criado.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | JID do canal ( |
nome | string | null | sim | |
descricao | string | null | sim | |
seguidores | integer | null | sim | |
convite | string | null | sim | Link de convite do canal. |
papel | string | null | sim | Papel do número no canal ( |
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.
/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)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
link | string | sim |
Exemplo de corpo
{
"numeroId": "clx0numero000000000000001",
"link": "https://whatsapp.com/channel/0029VaExemplo123456"
} Resposta 200 · OK
O canal.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | JID do canal ( |
nome | string | null | sim | |
descricao | string | null | sim | |
seguidores | integer | null | sim | |
convite | string | null | sim | Link de convite do canal. |
papel | string | null | sim | Papel do número no canal ( |
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.
/v1/canais/{canalId} Detalhes de um canal
Só números NAO_OFICIAL conectados (oficial → 422 recurso_indisponivel).
Parâmetros de caminho
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
canalId | string | sim |
|
Parâmetros de consulta (query string)
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
Resposta 200 · OK
O canal.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | JID do canal ( |
nome | string | null | sim | |
descricao | string | null | sim | |
seguidores | integer | null | sim | |
convite | string | null | sim | Link de convite do canal. |
papel | string | null | sim | Papel do número no canal ( |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
canalId | string | sim |
|
Parâmetros de consulta (query string)
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | sim | Identificador público do número ( |
quantidade | integer | não | |
antesDe | integer | não |
|
Resposta 200 · OK
As publicações.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
mensagens | object[] | sim | |
mensagens[].id | string | sim | |
mensagens[].servidorId | integer | sim | |
mensagens[].tipo | string | sim | |
mensagens[].texto | string | null | sim | |
mensagens[].enviadaEm | string | null | sim | |
mensagens[].visualizacoes | integer | sim | |
mensagens[].reacoes | object | sim | |
antesDe | integer | null | sim | Passe em |
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).
- get
/v1/numeros/{id}/templates - get
/v1/campanhas - post
/v1/campanhas - get
/v1/campanhas/{id} - post
/v1/campanhas/{id}/iniciar - post
/v1/campanhas/{id}/pausar - post
/v1/campanhas/{id}/retomar - post
/v1/campanhas/{id}/cancelar
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");
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador público do número OFICIAL. |
Resposta 200 · OK
Templates sincronizados da Meta.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
templates | object[] | sim | |
templates[].id | string | sim | Use em |
templates[].nome | string | sim | |
templates[].idioma | string | sim | |
templates[].categoria | enum | sim | Valores: |
templates[].status | string | sim | Status na Meta: APPROVED, PENDING, REJECTED, PAUSED, DISABLED ou INDISPONIVEL. |
templates[].motivo | string | null | sim | |
templates[].totalVariaveis | integer | sim | |
templates[].variaveis | object[] | sim | |
templates[].sincronizadoEm | string | sim |
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.
/v1/campanhas Lista as campanhas
Parâmetros de consulta (query string)
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
status | string | não | Valores: |
Resposta 200 · OK
Campanhas da conta (até 200, mais recentes primeiro).
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
campanhas | object[] | sim | |
campanhas[].id | string | sim | |
campanhas[].nome | string | sim | |
campanhas[].status | enum | sim | Valores: |
campanhas[].numero | object | sim | |
campanhas[].numero.id | string | sim | |
campanhas[].numero.apelido | string | sim | |
campanhas[].template | object | sim | |
campanhas[].template.id | string | sim | |
campanhas[].template.nome | string | sim | |
campanhas[].agendadaPara | string | null | sim | |
campanhas[].iniciadaEm | string | null | sim | |
campanhas[].concluidaEm | string | null | sim | |
campanhas[].custoEstimadoCentavos | integer | sim | |
campanhas[].total | integer | sim | |
campanhas[].totais | object | sim | |
campanhas[].totais.pendente | integer | sim | |
campanhas[].totais.enviado | integer | sim | |
campanhas[].totais.entregue | integer | sim | |
campanhas[].totais.lido | integer | sim | |
campanhas[].totais.respondido | integer | sim | |
campanhas[].totais.falhou | integer | sim | |
campanhas[].totais.ignoradoOptout | integer | sim | |
campanhas[].criadoEm | string | sim |
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.
/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
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
nome | string | sim | Tamanho: 1 a 120. |
numeroId | string | sim | Número OFICIAL. Tamanho: 1 a 64. |
templateId | string | sim |
|
listaId | string | não | Lista de contatos já criada no painel. Use |
contatos | object[] | não | Contatos inline (até 50.000), cada um com suas variáveis. No máximo 50.000 itens. |
contatos[].telefone | string | number | sim | Telefone com DDD (e DDI se não for do Brasil, com |
contatos[].nome | string | null | não | Tamanho: 0 a 200. |
contatos[].variaveis | string | number[] | não | Valores das variáveis do template para este contato, na ordem ( |
contatos[].campos | object | não | Campos extras, referenciáveis no |
mapeamento | object | não | Posição da variável ( |
mapeamento{chave}.tipo | enum | sim | Valores: |
mapeamento{chave}.valor | string | sim | Tamanho: 1 a 100. |
agendadaPara | string | não | Sem fuso explícito vale o horário de Brasília. |
pausaAutomatica | boolean | não | Padrão: |
iniciar | boolean | não | true (padrão) dispara agora; ignorado se houver |
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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
nome | string | sim | |
status | enum | sim | Valores: |
numero | object | sim | |
numero.id | string | sim | |
numero.apelido | string | sim | |
template | object | sim | |
template.id | string | sim | |
template.nome | string | sim | |
lista | object | null | sim | |
lista.id | string | sim | |
lista.nome | string | sim | |
agendadaPara | string | null | sim | |
iniciadaEm | string | null | sim | |
concluidaEm | string | null | sim | |
pausaAutomatica | boolean | sim | |
motivoPausa | string | null | sim | |
custoEstimadoCentavos | integer | sim | |
total | integer | sim | Contatos da campanha. |
totais | object | sim | |
totais.pendente | integer | sim | |
totais.enviado | integer | sim | |
totais.entregue | integer | sim | |
totais.lido | integer | sim | |
totais.respondido | integer | sim | |
totais.falhou | integer | sim | |
totais.ignoradoOptout | integer | sim | |
alertas | object[] | sim | Informativos; nunca bloqueiam. |
alertas[].codigo | string | sim | |
alertas[].mensagem | string | sim | |
taxaFalha | number | sim | Falhas ÷ (enviados + falhas), em %. |
erroMaisFrequente | object | null | sim | |
erroMaisFrequente.codigo | string | sim | |
erroMaisFrequente.mensagem | string | sim | |
erroMaisFrequente.quantidade | integer | sim | |
criadoEm | string | sim | |
estimativa | object | sim | |
estimativa.categoria | enum | sim | Valores: |
estimativa.quantidade | integer | sim | Contatos que receberão (exclui opt-out). |
estimativa.ignoradosOptout | integer | sim | |
estimativa.centavos | integer | sim | Estimativa do que a Meta cobra, em centavos de BRL. |
estimativa.porPais | object[] | sim | |
estimativa.porPais[].ddi | string | sim | |
estimativa.porPais[].rotulo | string | sim | |
estimativa.porPais[].quantidade | integer | sim | |
estimativa.porPais[].precoUsd | string | sim | |
estimativa.porPais[].centavosPorMensagem | integer | sim | |
estimativa.porPais[].centavos | integer | sim | |
estimativa.cambio | string | null | sim | |
estimativa.tabelaVersao | integer | null | sim | |
estimativa.semPreco | string[] | sim | |
estimativa.estimado | enum | sim | Valores: |
contatos | object | null | sim | Resumo da importação quando a campanha foi criada com |
contatos.recebidos | integer | sim | |
contatos.aceitos | integer | sim | |
contatos.rejeitados | integer | sim | |
contatos.duplicados | integer | sim | |
contatos.rejeitadasAmostra | object[] | sim | |
contatos.rejeitadasAmostra[].linha | integer | sim | |
contatos.rejeitadasAmostra[].telefone | string | sim | |
contatos.rejeitadasAmostra[].motivo | string | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da campanha. |
Resposta 200 · OK
A campanha.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
nome | string | sim | |
status | enum | sim | Valores: |
numero | object | sim | |
numero.id | string | sim | |
numero.apelido | string | sim | |
template | object | sim | |
template.id | string | sim | |
template.nome | string | sim | |
lista | object | null | sim | |
lista.id | string | sim | |
lista.nome | string | sim | |
agendadaPara | string | null | sim | |
iniciadaEm | string | null | sim | |
concluidaEm | string | null | sim | |
pausaAutomatica | boolean | sim | |
motivoPausa | string | null | sim | |
custoEstimadoCentavos | integer | sim | |
total | integer | sim | Contatos da campanha. |
totais | object | sim | |
totais.pendente | integer | sim | |
totais.enviado | integer | sim | |
totais.entregue | integer | sim | |
totais.lido | integer | sim | |
totais.respondido | integer | sim | |
totais.falhou | integer | sim | |
totais.ignoradoOptout | integer | sim | |
alertas | object[] | sim | Informativos; nunca bloqueiam. |
alertas[].codigo | string | sim | |
alertas[].mensagem | string | sim | |
taxaFalha | number | sim | Falhas ÷ (enviados + falhas), em %. |
erroMaisFrequente | object | null | sim | |
erroMaisFrequente.codigo | string | sim | |
erroMaisFrequente.mensagem | string | sim | |
erroMaisFrequente.quantidade | integer | sim | |
criadoEm | string | sim |
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.
/v1/campanhas/{id}/iniciar Inicia a campanha
Dispara uma campanha em rascunho ou agendada. Estado incompatível responde 409.
Parâmetros de caminho
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da campanha. |
Resposta 200 · OK
A campanha atualizada.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
nome | string | sim | |
status | enum | sim | Valores: |
numero | object | sim | |
numero.id | string | sim | |
numero.apelido | string | sim | |
template | object | sim | |
template.id | string | sim | |
template.nome | string | sim | |
lista | object | null | sim | |
lista.id | string | sim | |
lista.nome | string | sim | |
agendadaPara | string | null | sim | |
iniciadaEm | string | null | sim | |
concluidaEm | string | null | sim | |
pausaAutomatica | boolean | sim | |
motivoPausa | string | null | sim | |
custoEstimadoCentavos | integer | sim | |
total | integer | sim | Contatos da campanha. |
totais | object | sim | |
totais.pendente | integer | sim | |
totais.enviado | integer | sim | |
totais.entregue | integer | sim | |
totais.lido | integer | sim | |
totais.respondido | integer | sim | |
totais.falhou | integer | sim | |
totais.ignoradoOptout | integer | sim | |
alertas | object[] | sim | Informativos; nunca bloqueiam. |
alertas[].codigo | string | sim | |
alertas[].mensagem | string | sim | |
taxaFalha | number | sim | Falhas ÷ (enviados + falhas), em %. |
erroMaisFrequente | object | null | sim | |
erroMaisFrequente.codigo | string | sim | |
erroMaisFrequente.mensagem | string | sim | |
erroMaisFrequente.quantidade | integer | sim | |
criadoEm | string | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da campanha. |
Resposta 200 · OK
A campanha atualizada.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
nome | string | sim | |
status | enum | sim | Valores: |
numero | object | sim | |
numero.id | string | sim | |
numero.apelido | string | sim | |
template | object | sim | |
template.id | string | sim | |
template.nome | string | sim | |
lista | object | null | sim | |
lista.id | string | sim | |
lista.nome | string | sim | |
agendadaPara | string | null | sim | |
iniciadaEm | string | null | sim | |
concluidaEm | string | null | sim | |
pausaAutomatica | boolean | sim | |
motivoPausa | string | null | sim | |
custoEstimadoCentavos | integer | sim | |
total | integer | sim | Contatos da campanha. |
totais | object | sim | |
totais.pendente | integer | sim | |
totais.enviado | integer | sim | |
totais.entregue | integer | sim | |
totais.lido | integer | sim | |
totais.respondido | integer | sim | |
totais.falhou | integer | sim | |
totais.ignoradoOptout | integer | sim | |
alertas | object[] | sim | Informativos; nunca bloqueiam. |
alertas[].codigo | string | sim | |
alertas[].mensagem | string | sim | |
taxaFalha | number | sim | Falhas ÷ (enviados + falhas), em %. |
erroMaisFrequente | object | null | sim | |
erroMaisFrequente.codigo | string | sim | |
erroMaisFrequente.mensagem | string | sim | |
erroMaisFrequente.quantidade | integer | sim | |
criadoEm | string | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da campanha. |
Resposta 200 · OK
A campanha atualizada.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
nome | string | sim | |
status | enum | sim | Valores: |
numero | object | sim | |
numero.id | string | sim | |
numero.apelido | string | sim | |
template | object | sim | |
template.id | string | sim | |
template.nome | string | sim | |
lista | object | null | sim | |
lista.id | string | sim | |
lista.nome | string | sim | |
agendadaPara | string | null | sim | |
iniciadaEm | string | null | sim | |
concluidaEm | string | null | sim | |
pausaAutomatica | boolean | sim | |
motivoPausa | string | null | sim | |
custoEstimadoCentavos | integer | sim | |
total | integer | sim | Contatos da campanha. |
totais | object | sim | |
totais.pendente | integer | sim | |
totais.enviado | integer | sim | |
totais.entregue | integer | sim | |
totais.lido | integer | sim | |
totais.respondido | integer | sim | |
totais.falhou | integer | sim | |
totais.ignoradoOptout | integer | sim | |
alertas | object[] | sim | Informativos; nunca bloqueiam. |
alertas[].codigo | string | sim | |
alertas[].mensagem | string | sim | |
taxaFalha | number | sim | Falhas ÷ (enviados + falhas), em %. |
erroMaisFrequente | object | null | sim | |
erroMaisFrequente.codigo | string | sim | |
erroMaisFrequente.mensagem | string | sim | |
erroMaisFrequente.quantidade | integer | sim | |
criadoEm | string | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador da campanha. |
Resposta 200 · OK
A campanha atualizada.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
nome | string | sim | |
status | enum | sim | Valores: |
numero | object | sim | |
numero.id | string | sim | |
numero.apelido | string | sim | |
template | object | sim | |
template.id | string | sim | |
template.nome | string | sim | |
lista | object | null | sim | |
lista.id | string | sim | |
lista.nome | string | sim | |
agendadaPara | string | null | sim | |
iniciadaEm | string | null | sim | |
concluidaEm | string | null | sim | |
pausaAutomatica | boolean | sim | |
motivoPausa | string | null | sim | |
custoEstimadoCentavos | integer | sim | |
total | integer | sim | Contatos da campanha. |
totais | object | sim | |
totais.pendente | integer | sim | |
totais.enviado | integer | sim | |
totais.entregue | integer | sim | |
totais.lido | integer | sim | |
totais.respondido | integer | sim | |
totais.falhou | integer | sim | |
totais.ignoradoOptout | integer | sim | |
alertas | object[] | sim | Informativos; nunca bloqueiam. |
alertas[].codigo | string | sim | |
alertas[].mensagem | string | sim | |
taxaFalha | number | sim | Falhas ÷ (enviados + falhas), em %. |
erroMaisFrequente | object | null | sim | |
erroMaisFrequente.codigo | string | sim | |
erroMaisFrequente.mensagem | string | sim | |
erroMaisFrequente.quantidade | integer | sim | |
criadoEm | string | sim |
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; }
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
status | string | não | Valores: |
origem | string | não | Valores: |
numeroOficialId | string | não | |
numeroAtribuidoId | string | não | |
campanhaId | string | não | |
anuncioId | string | não | |
de | string | não | |
ate | string | não | |
limite | integer | não | |
cursor | string | não |
Resposta 200 · OK
Página de leads.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
itens | object[] | sim | |
itens[].id | string | sim | |
itens[].contatoTelefone | string | sim | |
itens[].contatoNome | string | null | sim | |
itens[].origem | enum | sim | Valores: |
itens[].campanha | object | null | sim | |
itens[].campanha.id | string | sim | |
itens[].campanha.nome | string | sim | |
itens[].anuncio | object | null | sim | |
itens[].anuncio.id | string | sim | |
itens[].anuncio.titulo | string | null | sim | |
itens[].anuncio.url | string | null | sim | |
itens[].primeiraMensagem | string | null | sim | |
itens[].status | enum | sim | Valores: |
itens[].numeroOficial | object | sim | |
itens[].numeroOficial.id | string | sim | |
itens[].numeroOficial.apelido | string | sim | |
itens[].numeroOficial.telefone | string | null | sim | |
itens[].numeroAtribuido | object | null | sim | |
itens[].numeroAtribuido.id | string | sim | |
itens[].numeroAtribuido.apelido | string | sim | |
itens[].numeroAtribuido.telefone | string | null | sim | |
itens[].conversaId | string | null | sim | |
itens[].contatoOptout | boolean | sim | |
itens[].repassadoEm | string | null | sim | |
itens[].respondeuEm | string | null | sim | |
itens[].criadoEm | string | sim | |
itens[].economia | object | sim | |
itens[].economia.qtd | integer | sim | |
itens[].economia.marketingCentavos | integer | sim | |
itens[].economia.utilityCentavos | integer | sim | |
proximoCursor | string | null | sim |
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.
/v1/leads/{id} Detalhe de um lead
Parâmetros de caminho
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do lead. |
Resposta 200 · OK
O lead.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
contatoTelefone | string | sim | |
contatoNome | string | null | sim | |
origem | enum | sim | Valores: |
campanha | object | null | sim | |
campanha.id | string | sim | |
campanha.nome | string | sim | |
anuncio | object | null | sim | |
anuncio.id | string | sim | |
anuncio.titulo | string | null | sim | |
anuncio.url | string | null | sim | |
primeiraMensagem | string | null | sim | |
status | enum | sim | Valores: |
numeroOficial | object | sim | |
numeroOficial.id | string | sim | |
numeroOficial.apelido | string | sim | |
numeroOficial.telefone | string | null | sim | |
numeroAtribuido | object | null | sim | |
numeroAtribuido.id | string | sim | |
numeroAtribuido.apelido | string | sim | |
numeroAtribuido.telefone | string | null | sim | |
conversaId | string | null | sim | |
contatoOptout | boolean | sim | |
repassadoEm | string | null | sim | |
respondeuEm | string | null | sim | |
criadoEm | string | sim | |
economia | object | sim | |
economia.qtd | integer | sim | |
economia.marketingCentavos | integer | sim | |
economia.utilityCentavos | integer | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do lead. |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
numeroId | string | não |
Exemplo de corpo
{
"numeroId": "clx0numero000000000000001"
} Resposta 200 · OK
O lead repassado.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
lead | object | sim | |
lead.id | string | sim | |
lead.contatoTelefone | string | sim | |
lead.contatoNome | string | null | sim | |
lead.origem | enum | sim | Valores: |
lead.campanha | object | null | sim | |
lead.campanha.id | string | sim | |
lead.campanha.nome | string | sim | |
lead.anuncio | object | null | sim | |
lead.anuncio.id | string | sim | |
lead.anuncio.titulo | string | null | sim | |
lead.anuncio.url | string | null | sim | |
lead.primeiraMensagem | string | null | sim | |
lead.status | enum | sim | Valores: |
lead.numeroOficial | object | sim | |
lead.numeroOficial.id | string | sim | |
lead.numeroOficial.apelido | string | sim | |
lead.numeroOficial.telefone | string | null | sim | |
lead.numeroAtribuido | object | null | sim | |
lead.numeroAtribuido.id | string | sim | |
lead.numeroAtribuido.apelido | string | sim | |
lead.numeroAtribuido.telefone | string | null | sim | |
lead.conversaId | string | null | sim | |
lead.contatoOptout | boolean | sim | |
lead.repassadoEm | string | null | sim | |
lead.respondeuEm | string | null | sim | |
lead.criadoEm | string | sim | |
lead.economia | object | sim | |
lead.economia.qtd | integer | sim | |
lead.economia.marketingCentavos | integer | sim | |
lead.economia.utilityCentavos | integer | sim | |
contatoOptout | boolean | sim |
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.
/v1/leads/{id}/descartar Descarta o lead
Idempotente. Cancela um repasse automático pendente.
Parâmetros de caminho
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do lead. |
Resposta 200 · OK
O lead descartado.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
contatoTelefone | string | sim | |
contatoNome | string | null | sim | |
origem | enum | sim | Valores: |
campanha | object | null | sim | |
campanha.id | string | sim | |
campanha.nome | string | sim | |
anuncio | object | null | sim | |
anuncio.id | string | sim | |
anuncio.titulo | string | null | sim | |
anuncio.url | string | null | sim | |
primeiraMensagem | string | null | sim | |
status | enum | sim | Valores: |
numeroOficial | object | sim | |
numeroOficial.id | string | sim | |
numeroOficial.apelido | string | sim | |
numeroOficial.telefone | string | null | sim | |
numeroAtribuido | object | null | sim | |
numeroAtribuido.id | string | sim | |
numeroAtribuido.apelido | string | sim | |
numeroAtribuido.telefone | string | null | sim | |
conversaId | string | null | sim | |
contatoOptout | boolean | sim | |
repassadoEm | string | null | sim | |
respondeuEm | string | null | sim | |
criadoEm | string | sim | |
economia | object | sim | |
economia.qtd | integer | sim | |
economia.marketingCentavos | integer | sim | |
economia.utilityCentavos | integer | sim |
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);
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
inicio | string | não | Padrão: primeiro dia do mês corrente (America/Sao_Paulo). |
fim | string | não | Padrão: agora. |
numeroId | string | não |
Resposta 200 · OK
Resumo da economia.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
periodo | object | sim | Dias (America/Sao_Paulo), |
periodo.inicio | string | sim | |
periodo.fim | string | sim | |
numeroId | string | null | sim | |
totais | object | sim | Os mesmos totais do dashboard, em centavos de BRL. |
totais.marketingCentavos | integer | sim | |
totais.utilityCentavos | integer | sim | |
totais.qtd | integer | sim | |
porTipoEnvio | object[] | sim | |
porTipoEnvio[].marketingCentavos | integer | sim | |
porTipoEnvio[].utilityCentavos | integer | sim | |
porTipoEnvio[].tipoEnvio | enum | sim | Valores: |
porTipoEnvio[].qtd | integer | sim | |
gastoOficial | object | sim | |
gastoOficial.centavos | integer | sim | |
gastoOficial.qtd | integer | sim | |
gastoOficial.porCategoria | object[] | sim | |
gastoOficial.porCategoria[].categoria | enum | sim | Valores: |
gastoOficial.porCategoria[].qtd | integer | sim | |
gastoOficial.porCategoria[].centavos | integer | sim | |
gastoOficial.estimado | enum | sim | Valores: |
porOrigemLead | object | sim | |
porOrigemLead.campanhas | object[] | sim | |
porOrigemLead.campanhas[].marketingCentavos | integer | sim | |
porOrigemLead.campanhas[].utilityCentavos | integer | sim | |
porOrigemLead.campanhas[].campanhaId | string | sim | |
porOrigemLead.campanhas[].nome | string | sim | |
porOrigemLead.campanhas[].qtd | integer | sim | |
porOrigemLead.anuncios | object[] | sim | |
porOrigemLead.anuncios[].marketingCentavos | integer | sim | |
porOrigemLead.anuncios[].utilityCentavos | integer | sim | |
porOrigemLead.anuncios[].anuncioId | string | sim | |
porOrigemLead.anuncios[].titulo | string | null | sim | |
porOrigemLead.anuncios[].qtd | integer | sim | |
tabelaPrecos | object | null | sim | |
tabelaPrecos.versao | integer | sim | |
tabelaPrecos.vigenteDe | string | sim | Data da tabela de preços da Meta usada no cálculo. |
tabelaPrecos.desatualizada | boolean | sim | |
tabelaPrecos.cambio | object | sim | |
tabelaPrecos.cambio.usdBrl | string | sim | |
tabelaPrecos.cambio.vigenteDe | string | sim | |
tabelaPrecos.cambio.fonte | string | sim |
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.
- get
/v1/destinos-eventos - post
/v1/destinos-eventos - post
/v1/destinos-eventos/entregas/{entregaId}/reenviar - get
/v1/destinos-eventos/{id} - patch
/v1/destinos-eventos/{id} - delete
/v1/destinos-eventos/{id} - post
/v1/destinos-eventos/{id}/testar - get
/v1/destinos-eventos/{id}/entregas
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');
/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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
destinos | object[] | sim | |
destinos[].id | string | sim | |
destinos[].tipo | enum | sim | Valores: |
destinos[].nome | string | sim | |
destinos[].ativo | boolean | sim | |
destinos[].eventos | enum[] | sim | |
destinos[].numeroIds | string[] | sim | Vazio = todos os números. |
destinos[].config | object | sim | |
destinos[].config.url | string | sim | |
destinos[].config.host | string | sim | |
destinos[].config.port | integer | sim | |
destinos[].config.vhost | string | sim | |
destinos[].config.usuario | string | sim | Mascarado. |
destinos[].config.senha | string | sim | Sempre mascarada. |
destinos[].config.exchange | string | sim | |
destinos[].config.prefixo | string | sim | |
destinos[].config.tls | boolean | sim | |
destinos[].config.permitirSemTls | boolean | sim | |
destinos[].config.modo | enum | sim | Valores: |
destinos[].config.servidores | string[] | sim | |
destinos[].config.autenticacao | enum | sim | Valores: |
destinos[].config.usuario | string | null | sim | Mascarado. |
destinos[].config.senha | string | null | sim | Sempre mascarada. |
destinos[].config.jetstream | boolean | sim | |
destinos[].config.brokers | string[] | sim | |
destinos[].config.sasl | enum | sim | Valores: |
destinos[].config.topico | string | null | sim | |
destinos[].config.prefixoTopico | string | null | sim | |
destinos[].config.filaUrl | string | sim | |
destinos[].config.regiao | string | sim | |
destinos[].config.fifo | boolean | sim | |
destinos[].config.accessKeyId | string | sim | Mascarado. |
destinos[].config.secretAccessKey | string | sim | Sempre mascarado. |
destinos[].segredoHmac | string | sim | Segredo de assinatura MASCARADO. O completo só aparece na criação/rotação. |
destinos[].criadoEm | string | sim | |
destinos[].atualizadoEm | string | sim |
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.
/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
| Header | Tipo | Obrig. | Descrição |
|---|---|---|---|
Idempotency-Key | string | nã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 |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
tipo | enum | sim | Valores: |
nome | string | sim | Tamanho: 1 a 80. |
ativo | boolean | não | Padrão: |
eventos | enum[] | sim | No máximo 29 itens. |
numeroIds | string[] | não | No máximo 100 itens. Padrão: |
url | string | sim | Tamanho: 1 a 2048. |
uri | string | não | Tamanho: 0 a 2048. |
host | string | não | Tamanho: 1 a 253. |
port | integer | não | Intervalo: 1 a 65535. |
vhost | string | não | Tamanho: 0 a 255. |
usuario | string | não | Tamanho: 1 a 255. |
senha | string | não | Tamanho: 1 a 1024. |
tls | boolean | não | |
exchange | string | sim | Tamanho: 1 a 255. |
prefixo | string | não | Tamanho: 0 a 64. Padrão: |
permitirSemTls | boolean | não | Padrão: |
modo | enum | não | Valores: |
servidores | string[] | sim | No máximo 5 itens. |
autenticacao | enum | não | Valores: |
jetstream | boolean | não | Padrão: |
brokers | string[] | sim | No máximo 10 itens. |
sasl | enum | não | Valores: |
topico | string | não | Tamanho: 1 a 200. |
prefixoTopico | string | não | Tamanho: 1 a 200. |
filaUrl | string | sim | Tamanho: 0 a 300. |
accessKeyId | string | sim | |
secretAccessKey | string | sim | 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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
tipo | enum | sim | Valores: |
nome | string | sim | |
ativo | boolean | sim | |
eventos | enum[] | sim | |
numeroIds | string[] | sim | Vazio = todos os números. |
config | object | sim | |
config.url | string | sim | |
config.host | string | sim | |
config.port | integer | sim | |
config.vhost | string | sim | |
config.usuario | string | sim | Mascarado. |
config.senha | string | sim | Sempre mascarada. |
config.exchange | string | sim | |
config.prefixo | string | sim | |
config.tls | boolean | sim | |
config.permitirSemTls | boolean | sim | |
config.modo | enum | sim | Valores: |
config.servidores | string[] | sim | |
config.autenticacao | enum | sim | Valores: |
config.usuario | string | null | sim | Mascarado. |
config.senha | string | null | sim | Sempre mascarada. |
config.jetstream | boolean | sim | |
config.brokers | string[] | sim | |
config.sasl | enum | sim | Valores: |
config.topico | string | null | sim | |
config.prefixoTopico | string | null | sim | |
config.filaUrl | string | sim | |
config.regiao | string | sim | |
config.fifo | boolean | sim | |
config.accessKeyId | string | sim | Mascarado. |
config.secretAccessKey | string | sim | Sempre mascarado. |
segredoHmac | string | sim | Segredo de assinatura MASCARADO. O completo só aparece na criação/rotação. |
criadoEm | string | sim | |
atualizadoEm | string | sim | |
segredoHmacRevelado | string | nã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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
entregaId | string | sim |
|
Resposta 200 · OK
A entrega reenfileirada.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
eventoId | string | sim | |
evento | string | sim | |
sequencia | integer | sim | |
status | enum | sim | Valores: |
tentativas | integer | sim | |
proximaTentativaEm | string | null | sim | |
ultimoErro | string | null | sim | |
entregueEm | string | null | sim | |
criadoEm | string | sim |
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.
/v1/destinos-eventos/{id} Detalhe de um destino
Parâmetros de caminho
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do destino. |
Resposta 200 · OK
O destino.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
tipo | enum | sim | Valores: |
nome | string | sim | |
ativo | boolean | sim | |
eventos | enum[] | sim | |
numeroIds | string[] | sim | Vazio = todos os números. |
config | object | sim | |
config.url | string | sim | |
config.host | string | sim | |
config.port | integer | sim | |
config.vhost | string | sim | |
config.usuario | string | sim | Mascarado. |
config.senha | string | sim | Sempre mascarada. |
config.exchange | string | sim | |
config.prefixo | string | sim | |
config.tls | boolean | sim | |
config.permitirSemTls | boolean | sim | |
config.modo | enum | sim | Valores: |
config.servidores | string[] | sim | |
config.autenticacao | enum | sim | Valores: |
config.usuario | string | null | sim | Mascarado. |
config.senha | string | null | sim | Sempre mascarada. |
config.jetstream | boolean | sim | |
config.brokers | string[] | sim | |
config.sasl | enum | sim | Valores: |
config.topico | string | null | sim | |
config.prefixoTopico | string | null | sim | |
config.filaUrl | string | sim | |
config.regiao | string | sim | |
config.fifo | boolean | sim | |
config.accessKeyId | string | sim | Mascarado. |
config.secretAccessKey | string | sim | Sempre mascarado. |
segredoHmac | string | sim | Segredo de assinatura MASCARADO. O completo só aparece na criação/rotação. |
criadoEm | string | sim | |
atualizadoEm | string | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do destino. |
Corpo da requisição (JSON)
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
nome | string | não | Tamanho: 1 a 80. |
ativo | boolean | não | |
eventos | enum[] | não | No máximo 29 itens. |
numeroIds | string[] | não | No máximo 100 itens. |
url | string | não | Tamanho: 1 a 2048. |
uri | string | não | Tamanho: 0 a 2048. |
host | string | não | Tamanho: 1 a 253. |
port | integer | não | Intervalo: 1 a 65535. |
vhost | string | não | Tamanho: 0 a 255. |
usuario | string | não | Tamanho: 1 a 255. |
senha | string | não | Tamanho: 1 a 8192. |
tls | boolean | não | |
exchange | string | não | Tamanho: 1 a 255. |
prefixo | string | não | Tamanho: 0 a 64. |
permitirSemTls | boolean | não | |
modo | enum | não | Valores: |
servidores | string[] | não | No máximo 5 itens. |
autenticacao | enum | não | Valores: |
jetstream | boolean | não | |
brokers | string[] | não | No máximo 10 itens. |
sasl | enum | não | Valores: |
topico | string | null | não | Tamanho: 1 a 200. |
prefixoTopico | string | null | não | Tamanho: 1 a 200. |
filaUrl | string | não | Tamanho: 0 a 300. |
accessKeyId | string | não | |
secretAccessKey | string | não | Tamanho: 16 a 256. |
rotacionarSegredo | boolean | não |
Exemplo de corpo
{
"ativo": false
} Resposta 200 · OK
O destino atualizado.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | |
tipo | enum | sim | Valores: |
nome | string | sim | |
ativo | boolean | sim | |
eventos | enum[] | sim | |
numeroIds | string[] | sim | Vazio = todos os números. |
config | object | sim | |
config.url | string | sim | |
config.host | string | sim | |
config.port | integer | sim | |
config.vhost | string | sim | |
config.usuario | string | sim | Mascarado. |
config.senha | string | sim | Sempre mascarada. |
config.exchange | string | sim | |
config.prefixo | string | sim | |
config.tls | boolean | sim | |
config.permitirSemTls | boolean | sim | |
config.modo | enum | sim | Valores: |
config.servidores | string[] | sim | |
config.autenticacao | enum | sim | Valores: |
config.usuario | string | null | sim | Mascarado. |
config.senha | string | null | sim | Sempre mascarada. |
config.jetstream | boolean | sim | |
config.brokers | string[] | sim | |
config.sasl | enum | sim | Valores: |
config.topico | string | null | sim | |
config.prefixoTopico | string | null | sim | |
config.filaUrl | string | sim | |
config.regiao | string | sim | |
config.fifo | boolean | sim | |
config.accessKeyId | string | sim | Mascarado. |
config.secretAccessKey | string | sim | Sempre mascarado. |
segredoHmac | string | sim | Segredo de assinatura MASCARADO. O completo só aparece na criação/rotação. |
criadoEm | string | sim | |
atualizadoEm | string | sim | |
segredoHmacRevelado | string | nã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.
/v1/destinos-eventos/{id} Remove um destino
Parâmetros de caminho
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | 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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do destino. |
Resposta 200 · OK
Resultado do teste.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
sucesso | boolean | sim | |
statusHttp | integer | null | sim | |
latenciaMs | integer | sim | |
erro | string | null | sim | |
aviso | string | null | sim |
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.
/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âmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Identificador do destino. |
Parâmetros de consulta (query string)
| Parâmetro | Tipo | Obrig. | Descrição |
|---|---|---|---|
pagina | integer | não | |
limite | integer | não | |
status | string | não | Valores: |
Resposta 200 · OK
Página de entregas.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
itens | object[] | sim | |
itens[].id | string | sim | |
itens[].eventoId | string | sim | |
itens[].evento | string | sim | |
itens[].sequencia | integer | sim | |
itens[].status | enum | sim | Valores: |
itens[].tentativas | integer | sim | |
itens[].proximaTentativaEm | string | null | sim | |
itens[].ultimoErro | string | null | sim | |
itens[].entregueEm | string | null | sim | |
itens[].criadoEm | string | sim | |
total | integer | sim | |
pagina | integer | sim | |
limite | integer | sim |
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.
/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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
eventos | object[] | sim | |
eventos[].nome | enum | sim | Valores: |
eventos[].descricao | string | sim | |
eventos[].tiposNumero | enum[] | sim | Tipos de número em que o evento ocorre. |
eventos[].versao | integer | sim | Versão do formato de |
tiposDestino | enum[] | sim | |
websocket | object | sim | |
websocket.url | string | sim | 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.
/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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
ticket | string | sim | Uso único. Conecte em |
expiraEmSegundos | integer | sim |
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.
| Campo | Tipo | Obrig. | Descrição |
|---|---|---|---|
id | string | sim | Único. Use para deduplicar (o mesmo evento vai com o mesmo |
evento | enum | sim | Valores: |
ocorridoEm | string | sim | |
contaId | string | sim | |
numeroId | string | null | sim | Nulo em eventos sem número. |
sequencia | integer | sim | Crescente por número: use para reordenar. Zero em eventos sem número. |
dados | object | sim | 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 |
numero.status | zaphub.numero.status | Conectado, desconectado, erro, restrito… |
numero.qrcode | zaphub.numero.qrcode | QR renovado durante o pareamento ( |
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 ( |
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. |
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 ( |
contato.digitando | zaphub.contato.digitando | O contato está digitando, gravando áudio ou parou ( |
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 ( |
numero.banido | zaphub.numero.banido | O WhatsApp aplicou banimento temporário ao número. Também sai |
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 |
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. |
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 ( |
etiqueta.aplicada | zaphub.etiqueta.aplicada | Uma etiqueta foi aplicada ( |
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 ( |
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: atributoassinatura(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) econtent_type = application/json; message_idigual aoiddo evento; headersx-zaphub-evento,x-zaphub-contaex-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;amqpsem 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_ROTEADAe o painel avisa; - modo filas (
"modo": "filas"): o ZapHub declara uma fila durável por evento assinado (<prefixo>.<evento>) já ligada à exchange, como oAMQP_SPECIFIC_EVENTSdo 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 usamconta), entãozaphub.*.NUMERO.>pega tudo de um número. Autenticação por token, usuário e senha ou arquivo.creds. Comjetstream: true, a publicação espera o ack do stream e é deduplicada peloNats-Msg-Id(igual aoiddo 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 é onumeroId: eventos do mesmo número caem na mesma partição, em ordem. Produtor idempotente comacks=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, oMessageGroupIdé o número e oMessageDeduplicationIdé oiddo evento (reenvio não duplica). Em fila padrão, reordene porsequencia. 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(oiddo ú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();