Visão geral
A API do AutoZap é uma camada REST sobre a WhatsApp Cloud API oficial da Meta. Você chama a nossa API com uma chave interna; nós chamamos a Meta com o token do número conectado — que nunca é exposto.
- URL base
https://eduzapi.com.br/api/v1- Formato
- JSON (UTF-8). Datas em ISO 8601 (UTC).
- Especificação
- OpenAPI 3 (YAML)
Autenticação
Crie uma chave em Painel → Chaves de API e envie-a no cabeçalho Authorization. A chave completa aparece uma única vez; guardamos apenas um hash.
Authorization: Bearer sk_live_SUA_CHAVE
Cada chave tem escopos. Uma chamada sem o escopo necessário retorna 403 insufficient_scope.
| Escopo | Permite |
|---|---|
messages:send | Enviar mensagens e marcar como lidas |
messages:read | Consultar mensagens e status de entrega |
channels:read | Listar números conectados |
templates:read | Listar modelos de mensagem |
templates:write | Criar e apagar modelos de mensagem |
profile:write | Editar o perfil comercial do número |
media:read | Baixar mídias recebidas |
media:write | Enviar arquivos de mídia |
conversations:read | Ler conversas do chat (plano API + Chat) |
usage:read | Consultar uso da API |
Modo de teste
Chaves sk_test_ passam por toda a validação, mas não chamam a Meta: a resposta traz "test_mode": true e um id wamid.TEST…. Use-as para montar fluxos sem gastar mensagens. Para enviar de verdade, use uma chave sk_live_.
Erros e limites
Todo erro segue o mesmo envelope. Informe o request_id ao suporte.
{
"error": {
"code": "meta_api_error",
"message": "A Meta recusou a operação.",
"request_id": "6f1c…",
"meta_error": { "code": 131047, "message": "Re-engagement message", "fbtrace_id": "A…" }
}
}
| HTTP | Código | Quando |
|---|---|---|
| 400 | validation_error, invalid_recipient… | Corpo inválido. O campo com problema vem em field. |
| 401 | missing_api_key, invalid_api_key | Chave ausente, inválida, expirada ou revogada. |
| 403 | insufficient_scope, plan_required | Escopo ou plano insuficiente. |
| 404 | channel_not_found | O número não existe nesta conta ou não está ativo. |
| 409 | idempotency_conflict, channel_unavailable | Requisição repetida em andamento, ou número sem credencial válida. |
| 429 | rate_limited, meta_api_error | Limite desta API (por chave) ou limite da Meta. |
| 502 | meta_api_error | A Meta recusou. Detalhes originais em meta_error. |
| 504 | meta_timeout | A Meta não respondeu a tempo. |
Limite padrão: 120 requisições por minuto por chave (configurável pelo operador da plataforma). Os limites de mensagens da Meta (por número e por qualidade) se aplicam à parte.
Idempotência
Envie Idempotency-Key (8–128 caracteres, único por mensagem) para repetir com segurança após um erro de rede ou timeout. Se a mesma chave chegar de novo, devolvemos a resposta original com Idempotent-Replayed: true — a mensagem não é reenviada.
Conta
Retorna a organização, o plano e os dados da chave usada.
Números
Lista os números ativos. Use o phone_number_id nos demais endpoints.
{ "data": [ { "phone_number_id": "1234567890", "display_phone_number": "+55 11 4000-0000",
"verified_name": "Minha Empresa", "quality_rating": "GREEN", "status": "active" } ] }
Enviar mensagem
Escopo messages:send. Responde 201 quando a Meta aceita a mensagem; o status final (entregue, lida, falhou) chega por webhook.
curl -X POST https://eduzapi.com.br/api/v1/whatsapp/PHONE_NUMBER_ID/messages \
-H "Authorization: Bearer sk_test_SUBSTITUA_PELA_SUA_CHAVE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-1234-aviso" \
-d '{"to": "5511999998888", "type": "text", "text": {"body": "Mensagem de teste"}}'
# Python
import requests
r = requests.post(
"https://eduzapi.com.br/api/v1/whatsapp/PHONE_NUMBER_ID/messages",
headers={"Authorization": "Bearer sk_live_SUA_CHAVE"},
json={"to": "5511999998888", "type": "text", "text": {"body": "Olá!"}},
timeout=30,
)
print(r.status_code, r.json())
// Node.js (18+)
const r = await fetch("https://eduzapi.com.br/api/v1/whatsapp/PHONE_NUMBER_ID/messages", {
method: "POST",
headers: { Authorization: "Bearer sk_live_SUA_CHAVE", "Content-Type": "application/json" },
body: JSON.stringify({ to: "5511999998888", type: "text", text: { body: "Olá!" } }),
});
console.log(r.status, await r.json());
Resposta:
{
"data": {
"id": "0b0d…", // id interno da mensagem
"wamid": "wamid.HBgM…", // id da Meta (aparece nos webhooks de status)
"status": "accepted",
"type": "text",
"contact_wa_id": "5511999998888",
"meta_response": { "messaging_product": "whatsapp", "contacts": [...], "messages": [...] }
},
"test_mode": false,
"request_id": "…"
}
type: "template"). Respeite o consentimento (opt-in) do destinatário e as políticas do WhatsApp Business.Tipos de mensagem
O corpo segue o objeto de mensagem da Cloud API — sem messaging_product, que nós adicionamos. Exemplos:
// Modelo aprovado com variáveis
{ "to": "5511999998888", "type": "template",
"template": { "name": "pedido_enviado", "language": { "code": "pt_BR" },
"components": [ { "type": "body", "parameters": [ { "type": "text", "text": "Ana" } ] } ] } }
// Imagem por link (ou por "id" de mídia enviada em /media)
{ "to": "5511999998888", "type": "image",
"image": { "link": "https://exemplo.com/foto.jpg", "caption": "Seu produto" } }
// Documento
{ "to": "5511999998888", "type": "document",
"document": { "link": "https://exemplo.com/boleto.pdf", "filename": "boleto.pdf" } }
// Botões de resposta
{ "to": "5511999998888", "type": "interactive",
"interactive": { "type": "button", "body": { "text": "Confirma o horário?" },
"action": { "buttons": [
{ "type": "reply", "reply": { "id": "sim", "title": "Sim" } },
{ "type": "reply", "reply": { "id": "nao", "title": "Não" } } ] } } }
// Localização
{ "to": "5511999998888", "type": "location",
"location": { "latitude": -23.561, "longitude": -46.656, "name": "Loja Paulista" } }
// Reação a uma mensagem recebida
{ "to": "5511999998888", "type": "reaction",
"reaction": { "message_id": "wamid.HBgM…", "emoji": "👍" } }
// Responder citando uma mensagem
{ "to": "5511999998888", "type": "text", "text": { "body": "Claro!" },
"context": { "message_id": "wamid.HBgM…" } }
Tipos aceitos: text, template, image, video, audio, document, sticker, location, interactive, reaction, contacts.
Marcar como lida
{ "message_id": "wamid.HBgM…" }
Consultar mensagens
Escopo messages:read. Retorna status de entrega e erros. O campo content só é preenchido no plano API + Chat (no plano API o conteúdo não é armazenado; você o recebe no webhook). Para paginar, envie before = next_before da página anterior.
Modelos
Lista os modelos da conta do WhatsApp (WABA) diretamente na Meta. Modelos são criados no painel (página do número) ou no WhatsApp Manager e passam por análise da Meta.
Mídia
Upload multipart/form-data com o arquivo no campo file. Retorna {"data": {"id": "…"}} para usar em image.id, document.id etc.
curl -X POST https://eduzapi.com.br/api/v1/whatsapp/PHONE_NUMBER_ID/media \
-H "Authorization: Bearer sk_live_SUA_CHAVE" -F "file=@foto.jpg;type=image/jpeg"
Baixa (stream) uma mídia recebida — o media_id chega no webhook message.received. A Meta mantém mídias recebidas por tempo limitado; baixe e guarde no seu sistema se precisar.
Conversas (plano API + Chat)
Escopo conversations:read. Inclui window_open (janela de 24h aberta).
Uso
Contagens reais de requisições e mensagens no período. A cobrança das conversas/mensagens é feita pela Meta diretamente na conta do WhatsApp do negócio.
Webhooks — eventos
Cadastre uma URL HTTPS em Painel → Webhooks e escolha os eventos. Enviamos um POST JSON; responda com qualquer 2xx em até 10 s. Falhas são repetidas com espera crescente (2, 4, 8… minutos) até 6 tentativas.
| Evento | Descrição |
|---|---|
message.received | Mensagem recebida de um contato |
message.status.updated | Status de mensagem enviada (enviada, entregue, lida, falhou) |
message.sent | Mensagem enviada pela API ou pelo painel |
channel.connected | Número conectado |
channel.disconnected | Número desconectado |
template.status.updated | Modelo de mensagem aprovado/rejeitado pela Meta |
api.request.failed | Requisição à API recusada pela Meta |
POST https://seu-sistema.com/webhook
X-Webhook-Id: 9b2e… (id da entrega — use para deduplicar)
X-Webhook-Event: message.received
X-Webhook-Timestamp: 1767225600
X-Webhook-Signature: sha256=5d1c…
{
"id": "9b2e…",
"event": "message.received",
"created_at": "2026-10-10T13:00:00+00:00",
"data": {
"phone_number_id": "1234567890",
"contact": { "wa_id": "5511999998888", "name": "Ana" },
"message": { "id": "wamid.HBgM…", "from": "5511999998888", "timestamp": "1767225600",
"type": "text", "text": { "body": "Olá!" } }
}
}
Validar a assinatura
Calcule HMAC-SHA256(segredo, timestamp + "." + corpo_bruto) e compare com o cabeçalho em tempo constante. Rejeite timestamps com mais de 5 minutos para evitar replay. O segredo whsec_… aparece uma vez ao criar o webhook.
# Python (Flask)
import hashlib, hmac, time
from flask import request, abort
SECRET = "whsec_SEU_SEGREDO"
def verificar():
ts = request.headers.get("X-Webhook-Timestamp", "")
assinatura = request.headers.get("X-Webhook-Signature", "")
if not ts.isdigit() or abs(time.time() - int(ts)) > 300:
abort(400)
esperado = "sha256=" + hmac.new(SECRET.encode(), ts.encode() + b"." + request.get_data(),
hashlib.sha256).hexdigest()
if not hmac.compare_digest(esperado, assinatura):
abort(401)
// Node.js (Express com express.raw({ type: "application/json" }))
const crypto = require("crypto");
function verificar(req) {
const ts = req.get("X-Webhook-Timestamp");
const esperado = "sha256=" + crypto.createHmac("sha256", process.env.WEBHOOK_SECRET)
.update(ts + "." + req.body).digest("hex");
const recebido = req.get("X-Webhook-Signature") || "";
return Math.abs(Date.now() / 1000 - Number(ts)) < 300 &&
recebido.length === esperado.length &&
crypto.timingSafeEqual(Buffer.from(esperado), Buffer.from(recebido));
}
Exemplo com n8n
- Crie um nó Webhook (método POST) e copie a URL de produção.
- No painel, cadastre essa URL em Webhooks com o evento
message.received. - Processe
{{ $json.body.data.message.text.body }}(texto recebido) e o remetente emdata.contact.wa_id. - Para responder, use um nó HTTP Request: POST para
https://eduzapi.com.br/api/v1/whatsapp/PHONE_NUMBER_ID/messages, cabeçalhoAuthorization: Bearer sk_live_…e corpo JSON como nos exemplos acima.