API REST
A API do BeeZap segue REST simples sobre HTTPS. Toda chamada é autenticada via Authorization: Bearer com a chave do cliente.
Base URL
A base de todas as chamadas é https://app.bee-zap.com. Os endpoints ficam em https://app.bee-zap.com/api/v1/... — por exemplo, https://app.bee-zap.com/api/v1/messages e https://app.bee-zap.com/api/v1/contacts.
API key no header Authorization: Bearer. Nos exemplos abaixo, onde aparecer HUB_URL, use https://app.bee-zap.com.Autenticação
Inclua o header Authorization: Bearer SUA_API_KEY. A chave começa com bz_ (chaves antigas começam com zh_ — ambas continuam válidas).
curl https://app.bee-zap.com/api/v1/messages/123 \
-H "Authorization: Bearer bz_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"Respostas de erro de autenticação:
| Status | Motivo |
|---|---|
| 401 | Header ausente, vazio ou chave inválida |
| 403 | Cliente inativo |
POST /v1/messages
Dispara uma mensagem WhatsApp pelo hub.
Headers
| Header | Obrigatório | Descrição |
|---|---|---|
| authorization | sim | Bearer SUA_API_KEY |
| content-type | sim | application/json |
| idempotency-key | opcional | Identificador único do request. Se enviar a mesma key duas vezes, a 2ª chamada não duplica — devolve a mensagem original. |
Body
| Campo | Tipo | Descrição |
|---|---|---|
| chatId | string | JID do destinatário no WhatsApp. Ex: 5511999999999@c.us pra contatos, 1234567890@g.us pra grupos. |
| text | string? | Texto da mensagem (até 4096 chars). Obrigatório se não enviar mediaUrl. |
| mediaUrl | string? | URL pública de uma imagem/vídeo/áudio/documento. |
| mediaType | enum? | IMAGE | VIDEO | AUDIO | DOCUMENT. Obrigatório se mediaUrl for usado. |
| caption | string? | Legenda da mídia (até 1024 chars). |
| sessionId | string? | Força o envio por uma sessão específica (bypassa o router). Use só se souber o que tá fazendo — geralmente o router escolhe melhor. |
| sessionName | string? | Idem, mas por nome humano da sessão. |
| poolStrategy | enum? | STICKY | LEAST_USED | ROUND_ROBIN. Sobrescreve a estratégia padrão do cliente pra esta chamada. |
Resposta de sucesso
{
"messageId": "cmod1828ze00d2bl8sn3kr0w",
"externalMessageId": "true_5511999999999@c.us_3EB0...",
"wahaMessageId": "true_5511999999999@c.us_3EB0...",
"status": "SENT",
"session": {
"id": "ckxx...",
"name": "Zoom Principal",
"engine": "WAHA",
"routedReason": "sticky"
}
}wahaMessageId é um alias depreciado e idêntico a externalMessageId. Use o novo nome em código novo.
Use messageId pra consultar status depois (acks de entrega/leitura chegam pelo webhook ou via GET).
Erros possíveis
| Status | Erro | Quando |
|---|---|---|
| 400 | validação | Body inválido (campo faltando, tipo errado). |
| 404 | Session not found | Você passou sessionId/sessionName que não pertence ao cliente. |
| 409 | Idempotency key conflict | Mesma idempotency-key foi usada em outro cliente. |
| 409 | Session not connected | A sessão escolhida não tá CONNECTED. |
| 429 | Rate limit exceeded | Cota do cliente (msgs/min) estourada. Header retry-after indica espera. |
| 503 | No session available | Nenhuma sessão CONNECTED disponível (todas em cooldown ou daily cap atingido). Body detalha: no_sessions, all_in_cooldown ou all_at_cap. |
| 502 | Engine upstream | Falha conversando com a engine WhatsApp. O hub registra como FAILED e marca a sessão se passar do limite de erros consecutivos. |
idempotency-key determinística (ex: new-lead-{leadId}). Se o seu sistema retentar por timeout, o BeeZap não duplica.GET /v1/messages/:id
Consulta status atual de uma mensagem.
curl https://app.bee-zap.com/api/v1/messages/cmod1828ze00d2bl8sn3kr0w \
-H "Authorization: Bearer bz_xxx..."{
"id": "cmod1828ze00d2bl8sn3kr0w",
"chatId": "5511999999999@c.us",
"direction": "OUTBOUND",
"status": "READ",
"externalMessageId": "true_5511...",
"wahaMessageId": "true_5511...",
"failReason": null,
"sentAt": "2026-04-27T13:42:01.123Z",
"deliveredAt": "2026-04-27T13:42:03.001Z",
"readAt": "2026-04-27T13:42:30.220Z",
"createdAt": "2026-04-27T13:42:00.998Z"
}Em geral, prefira receber acks via webhook em vez de polling.
POST /v1/contacts
Cadastra (ou enriquece) um contato e, por padrão, cria um lead no CRM. Idempotente por número de WhatsApp: chamar de novo com o mesmo telefone não duplica — atualiza o contato existente. Útil pra integrar um sistema externo (CRM, formulário, ERP) que empurra leads pro BeeZap.
Headers
| Header | Obrigatório | Descrição |
|---|---|---|
| authorization | sim | Bearer SUA_API_KEY |
| content-type | sim | application/json |
Body
| Campo | Tipo | Descrição |
|---|---|---|
| phone | string? | Telefone em qualquer formato (ex: 11999998888, +55 11 99999-8888). DDI/DDD/9º dígito são normalizados. Obrigatório se não enviar whatsappJid. |
| whatsappJid | string? | JID pronto (5511999998888@c.us). Alternativa ao phone. |
| name | string? | Nome do contato. Se vazio, usa o telefone formatado. |
| string? | E-mail do contato. | |
| company | string? | Empresa. |
| document | string? | CPF/CNPJ ou outro documento. |
| notes | string? | Observações livres. |
| tags | string[]? | Tags pra aplicar ao contato (criadas se não existirem). Não dispara automações de TAG_ADDED. |
| createLead | boolean? | Cria também um card no CRM (pipeline/estágio padrão). Default true. |
| source | string? | Origem registrada no card (default api). |
| triggerAutomations | boolean? | Se true, as tags enviadas disparam as automações ativas com gatilho TAG_ADDED daquela tag — útil pra um evento externo iniciar um fluxo (ex: boas-vindas). Default false. |
TAG_ADDED). Aí o sistema externo chama POST /v1/contacts com essa tag e triggerAutomations: true — o fluxo começa para o contato. Funciona tanto pra contato novo quanto pra um já existente (a chamada é idempotente). Já há proteção contra rodar o mesmo fluxo em duplicado pro mesmo contato.Resposta
201 quando o contato é novo, 200 quando já existia (enriquecido).
{
"contactId": "cmod1828ze00d2bl8sn3kr0w",
"name": "Maria",
"whatsappJid": "5511999998888@c.us",
"phone": "5511999998888",
"created": true,
"lead": { "leadId": "cmod...", "stageId": "cmod...", "created": true }
}Criar contato/lead dispara os webhooks CONTACT_CREATED e LEAD_CREATED (veja a página de Webhooks) — então um sistema externo pode reagir à criação.
Exemplo (curl)
curl https://app.bee-zap.com/api/v1/contacts \
-X POST \
-H "authorization: Bearer bz_xxx..." \
-H "content-type: application/json" \
-d '{"phone":"11999998888","name":"Maria","email":"maria@ex.com","tags":["vip"]}'POST /v1/events
Registra um evento personalizado (KPI) que aparece no dashboard automaticamente. Útil pra acompanhar compra, abandonado, ticket fechado, etc — com contagem por período e delta versus o anterior. Cada nome novo vira um card.
curl https://app.bee-zap.com/api/v1/events \
-X POST \
-H "authorization: Bearer bz_xxx..." \
-H "content-type: application/json" \
-H "idempotency-key: pedido-12345" \
-d '{
"name": "compra",
"value": 199.90,
"chatId": "5511999998888@c.us",
"metadata": { "product": "premium" }
}'nameobrigatório, lowercase, regex^[a-z][a-z0-9_-]{0,63}$.occurredAtopcional (ISO-8601 com offset). Default: agora.valueopcional (decimal); usado pra somar "quanto" (ex: ticket).chatIdopcional pra correlacionar com uma conversa.metadataopcional (JSON livre).- Header
Idempotency-Keyopcional → segunda chamada com a mesma key retorna 409. - Cap de 200 nomes distintos por tenant. Rate limit: 120 eventos/min/tenant.
Rate limits
- Por cliente: definido pelo admin do hub (default 60 msgs/min). Excede → 429 com
retry-after: 60. - Por sessão WhatsApp: cota diária (default 100/dia, ramp do warmup). Estouro retorna 503.
Exemplos
Node.js
async function sendWhatsApp(phone, text) {
const res = await fetch(`${process.env.HUB_URL}/api/v1/messages`, {
method: "POST",
headers: {
"content-type": "application/json",
authorization: `Bearer ${process.env.HUB_API_KEY}`,
"idempotency-key": `lead-${phone}-${Date.now()}`,
},
body: JSON.stringify({
chatId: `${phone.replace(/\D/g, "")}@c.us`,
text,
}),
});
if (!res.ok) throw new Error(`hub ${res.status}: ${await res.text()}`);
return res.json();
}PHP
<?php
function sendWhatsApp(string $phone, string $text): array {
$payload = [
'chatId' => preg_replace('/\D/', '', $phone) . '@c.us',
'text' => $text,
];
$ch = curl_init(getenv('HUB_URL') . '/api/v1/messages');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'content-type: application/json',
'authorization: Bearer ' . getenv('HUB_API_KEY'),
'idempotency-key: lead-' . $phone . '-' . time(),
],
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_RETURNTRANSFER => true,
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($code >= 400) throw new RuntimeException("hub $code: $body");
return json_decode($body, true);
}Python
import os, requests, time
def send_whatsapp(phone: str, text: str) -> dict:
digits = "".join(c for c in phone if c.isdigit())
res = requests.post(
f"{os.environ['HUB_URL']}/api/v1/messages",
headers={
"authorization": f"Bearer {os.environ['HUB_API_KEY']}",
"content-type": "application/json",
"idempotency-key": f"lead-{phone}-{int(time.time())}",
},
json={"chatId": f"{digits}@c.us", "text": text},
timeout=15,
)
res.raise_for_status()
return res.json()