API Documentation
Integre a plataforma Agilizai ao seu backend. Crie campanhas, gerencie webhooks, consulte limites operacionais e controle conexões de canal via API.
Primeiros Passos
Siga estes 6 passos para começar a integrar com a API Agilizai em poucos minutos.
Passo 1.Gere sua API Key
Acesse Configurações > API Keys no dashboard, crie uma nova chave e selecione os escopos necessários para sua integração.
Abrir API KeysPasso 2.Teste a conexão
Faça uma requisição GET para /v1/limits com sua chave para validar autenticação e permissões.
Ver exemplo de requestPasso 3.Conecte seu WhatsApp
Crie uma instância via POST /v1/whatsapp/instances, escaneie o QR code com GET .../qr e aguarde a conexão via polling em GET .../status.
Ver fluxo de conexãoPasso 4.Valide modalidade e limites
Confirme se a empresa está habilitada (mensal ou avulso) e verifique limite diário, concorrência e RPM no endpoint /v1/limits.
Ir para limitesPasso 5.Crie uma campanha
Use o endpoint POST /v1/campaigns para criar sua primeira campanha de envio em massa com sua lista de contatos.
Ver payload completoPasso 6.Acompanhe o status
Monitore o andamento via GET /v1/campaigns/:id e /v1/campaigns/:id/messages ou configure webhooks para eventos em tempo real.
Configurar webhooksAutenticação
Todas as requisições devem incluir sua API key no header Authorization.
GET /v1/limits HTTP/1.1
Host: api-worker.agilizai.tech
Authorization: Bearer ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/jsonTipos de API Key
ak_live_xxxxxxxx...- Envia mensagens reais via WhatsApp
- Usa os limites e regras reais da empresa
- Dispara webhooks normalmente
- Use em ambientes de producao
ak_test_xxxxxxxx...- Não envia mensagens reais ao WhatsApp
- Simula envio com 95% sucesso e 5% falha
- Não consome creditos da empresa
- Use para testar integracoes com segurança
Ciclo de vida da API Key
Criar: Acesse Configurações > API Keys no dashboard. Selecione o nome e os scopes necessários. A key completa e exibida apenas uma vez. Copie e armazene em local seguro.
Rotacionar: Gere uma nova key sem invalidar a antiga imediatamente. Atualize seus sistemas e depois revogue a key antiga.
Revogar: Invalide uma key imediatamente. Requisicoes com essa key retornarao 401 Unauthorized.
Scopes e Permissoes
Cada API key possui scopes que definem quais endpoints podem ser acessados. Selecione apenas os necessários (principio do menor privilegio).
| Scope | Descrição | Endpoints |
|---|---|---|
messages:write | Enviar mensagens individuais | POST /v1/messages |
campaigns:read | Ler campanhas e status de mensagens | GET /v1/campaigns, GET /v1/campaigns/:id, GET /v1/campaigns/:id/messages |
campaigns:write | Criar, pausar, retomar e cancelar campanhas | POST /v1/campaigns, POST /v1/campaigns/:id/cancel, POST /v1/campaigns/:id/pause, POST /v1/campaigns/:id/resume |
webhooks:manage | Gerenciar webhooks | POST/GET/PUT/DELETE /v1/webhooks, POST /v1/webhooks/:id/test |
whatsapp:manage | Gerenciar instancias WhatsApp | POST/GET /v1/whatsapp/instances, GET /v1/whatsapp/instances/:id/qr, GET /v1/whatsapp/instances/:id/status, POST /v1/whatsapp/instances/:id/disconnect |
credits:read | Consultar limites e capacidade operacional | GET /v1/limits |
imports:write | Enfileirar importacao de contatos (uso interno) | POST /v1/imports |
tickets:read | Ler tickets, detalhe e historico de mensagens | GET /v1/tickets, GET /v1/tickets/:id, GET /v1/tickets/:id/messages |
tickets:write | Abrir/fechar, atribuir, etiquetar, vincular e transbordar tickets | POST /v1/tickets, PATCH /v1/tickets/:id/status|assignee|priority, POST/DELETE /v1/tickets/:id/tags, POST /v1/tickets/:id/links, POST /v1/tickets/:id/handoff |
contacts:read | Listar/buscar contatos e ler custom fields | GET /v1/contacts, GET /v1/contacts/:id, GET /v1/contact-fields |
contacts:write | Criar/atualizar contato, custom_fields e pausar/retomar a IA | POST /v1/contacts, PATCH /v1/contacts/:id, PATCH /v1/contacts/:id/assistant |
appointments:read | Tipos, disponibilidade e listagem de agendamentos | GET /v1/appointment-types, GET /v1/appointments/availability, GET /v1/appointments |
appointments:write | Criar, reagendar, cancelar e marcar comparecimento | POST /v1/appointments, PATCH /v1/appointments/:id, DELETE /v1/appointments/:id |
crm:read | Listar etapas e oportunidades do funil | GET /v1/crm/stages, GET /v1/crm/opportunities |
crm:write | Criar, editar, mover etapa e remover oportunidades | POST /v1/crm/opportunities, PATCH /v1/crm/opportunities/:id, POST /v1/crm/opportunities/:id/move-stage, DELETE /v1/crm/opportunities/:id |
scheduled_messages:read | Listar e consultar mensagens agendadas | GET /v1/scheduled-messages, GET /v1/scheduled-messages/:id |
scheduled_messages:write | Agendar e cancelar mensagens 1:1 | POST /v1/scheduled-messages, DELETE /v1/scheduled-messages/:id |
tags:read | Listar tags e regras de auto-tag | GET /v1/tags, GET /v1/tag-rules |
tags:write | Criar, editar e remover tags e regras de auto-tag | POST/PATCH/DELETE /v1/tags/:id, POST/PATCH/DELETE /v1/tag-rules/:id |
contracts:read | Listar contratos e modelos | GET /v1/contracts, GET /v1/contracts/:id, GET /v1/contract-templates |
contracts:write | Criar rascunho, editar e cancelar contratos (envio pela plataforma) | POST/PATCH/DELETE /v1/contracts/:id |
setor_id— useGET /v1/whatsapp/instancespara listar os setores com seus IDs.client_id— e retornado ao criar contatos ou pode ser consultado no painel de Contatos do dashboard.
Segurança da API Key
- Nunca exponha sua key em código frontend, repositorios públicos ou logs.
- Use variáveis de ambiente para armazenar keys no servidor.
- Rotacione periodicamente: recomendamos a cada 90 dias.
- Revogue imediatamente se suspeitar de vazamento.
- Restrinja scopes: uma key para criar campanhas não precisa de
whatsapp:manage.
Endpoints
Referência completa dos endpoints disponíveis, organizados por categoria.
Messages
8 endpoints/v1/messages--Enviar mensagem única de WhatsApp (síncrono)/v1/messages/first-contact--Enviar primeira mensagem com criação automatica de contato e afinidade/v1/messages/interactive--Enviar mensagem interativa com botões ou lista de opções/v1/messages/event--Enviar evento de calendario nativo do WhatsApp/v1/messages/poll--Enviar enquete com opções clicáveis/v1/messages/:conversa_id--Apagar mensagem enviada (soft delete) — números conectados por QR Code; não debita crédito/v1/messages/:conversa_id--Editar o texto de mensagem enviada (até ~15min) — números conectados por QR Code/v1/messages/:conversa_id/reaction--Reagir a mensagem com emoji (emoji vazio remove a reação)Campaigns
9 endpoints/v1/campaigns--Criar nova campanha de envio em massa/v1/campaigns--Listar todas as campanhas/v1/campaigns/:id--Detalhes de uma campanha/v1/campaigns/:id/messages--Listar mensagens de uma campanha/v1/campaigns/:id/recipients--Listar destinatários com status individual (filtro por status e busca q)/v1/campaigns/:id/cancel--Cancelar campanha em andamento/v1/campaigns/:id/pause--Pausar campanha em andamento/v1/campaigns/:id/resume--Retomar campanha pausada/v1/campaigns/:id/retry--Retentar mensagens que falharam (detailIds[] ou all_failures; caps manual=2/auto=5)Webhooks
5 endpoints/v1/webhooks--Registrar novo webhook/v1/webhooks--Listar webhooks configurados/v1/webhooks/:id--Atualizar webhook existente/v1/webhooks/:id--Remover webhook/v1/webhooks/:id/test--Enviar evento de teste ao webhook (timeout: 5s)Limites
1 endpoints/v1/limits--Consultar limites da empresa (diario, simultâneo, rate limit)/v1/whatsapp/instances--Criar nova instancia WhatsApp/v1/whatsapp/instances--Listar instancias WhatsApp/v1/whatsapp/instances/:id/qr--Obter QR Code para conexao/v1/whatsapp/instances/:id/status--Status da instancia WhatsApp/v1/whatsapp/instances/:id/disconnect--Desconectar instancia WhatsApp/v1/whatsapp/instances/health--Consultar saude das instancias (snapshot e historico)/v1/whatsapp/instances/:id/profile/refresh--Ressincronizar foto e nome de perfil da instância/v1/whatsapp/instances/profile/batch--Atualizar perfil (nome e/ou foto) de até 20 números de uma vez/v1/whatsapp/instances/profile-picture/batch--Atualizar só a foto de perfil em lote (legada — prefira profile/batch)Imports
1 endpoints/v1/imports--Enfileirar importacao assincrona de contatos (job_id para acompanhar)Leads
6 endpoints/v1/leads/cnaes--Autocomplete de CNAEs — setores de atividade (q, até 20 resultados)/v1/leads/municipios--Autocomplete de municípios IBGE (q, até 30 resultados)/v1/leads/search--Enfileirar busca de empresas (assíncrono, 202 + jobId)/v1/leads/save-to-crm--Salvar leads encontrados como contatos no CRM (upsert, até 200)/v1/leads/validate-whatsapp--Validar em lote se números têm WhatsApp (até 200)/v1/leads/validate-email-mx--Validar entregabilidade de e-mails por registro MX (até 200)Tickets
12 endpoints/v1/tickets--Listar tickets (cursor keyset; filtros: status CSV, setor, contato, dono, prioridade, updated_since, q)/v1/tickets--Abrir/reabrir ticket p/ contato (ativo → 409 com ticket_id; sem conversa → 422, use first-contact)/v1/tickets/:id--Detalhe do ticket + tags + vinculos (links.crm_opportunities)/v1/tickets/:id/messages--Timeline do atendimento (cursor) — o contexto que o bot le antes de responder/v1/tickets/:id/messages--Responder no ticket (debito Envios + refund; Meta 24h → 422 WINDOW_EXPIRED); private=true cria nota interna/v1/tickets/:id/status--Transicionar status (open/pending/snoozed/closed) — invalida → 409 INVALID_TRANSITION com allowed[]; fechar dispara msg de encerramento do setor/v1/tickets/:id/assignee--Atribuir agente/time (UUID ou null pra desatribuir)/v1/tickets/:id/priority--Alterar prioridade (low/medium/high/urgent/null)/v1/tickets/:id/tags--Etiquetar ticket (tag_ids[], idempotente)/v1/tickets/:id/tags--Remover etiquetas do ticket/v1/tickets/:id/links--Vincular ticket a oportunidade do CRM (type: crm_opportunity)/v1/tickets/:id/handoff--Transbordo bot → humano: abre p/ time/agente + resumo pro atendente + pausa a IA do contatoContacts
6 endpoints/v1/contacts--Listar/buscar contatos (por telefone com variantes do 9º dígito, q, ia_ativa)/v1/contacts--Criar/atualizar contato (upsert por telefone; fill-only no existente)/v1/contacts/:id--Detalhe 360: custom_fields + tickets ativos + próximos agendamentos/v1/contacts/:id--Atualizar nome/email/custom_fields (validados contra as definições)/v1/contacts/:id/assistant--Pausar/retomar a IA do contato (kill-switch que o n8n lê)/v1/contact-fields--Definições dos custom fields (schema pro bot antes de gravar)Appointments
9 endpoints/v1/appointment-types--Tipos/serviços com duração e unidades vinculadas/v1/appointments/availability--Slots livres (motor anti-conflito; escopo profissional/unidade/empresa)/v1/appointments--Listar agendamentos por período/contato (cursor)/v1/appointments--Criar (anti-conflito 409 SLOT_TAKEN + limite do plano + vínculo ticket_id); lembretes automáticos/v1/appointments/:id--Detalhe + status dos lembretes enviados/v1/appointments/:id--Reagendar (409 APPOINTMENT_CONFLICT), marcar comparecimento ou editar descrição/v1/appointments/:id--Cancelar agendamento/v1/professionals--Profissionais + habilitações (serviço×unidade) — discovery do profissional_id/v1/units--Unidades de atendimento com endereço — discovery do unidade_idReports
2 endpoints/v1/reports/summary--Métricas do período: mensagens, atendimento (tempo de resposta), campanhas e CSAT/v1/ratings--Avaliações (CSAT 1-5): resumo + ranking por atendenteCRM
7 endpoints/v1/crm/stages--Etapas do funil (is_won/is_lost/win_probability)/v1/crm/opportunities--Listar oportunidades (filtros stage/assignee/cliente/include_closed)/v1/crm/opportunities--Criar oportunidade (products[] p/ N produtos; ticket_id vincula à conversa)/v1/crm/opportunities/:id--Detalhe da oportunidade/v1/crm/opportunities/:id--Editar (title/value/assignee/products replace-all/descrição) + histórico/v1/crm/opportunities/:id/move-stage--Mover etapa → won/lost/stage_changed (fecha se stage final)/v1/crm/opportunities/:id--Remover oportunidadeScheduled Messages
4 endpoints/v1/scheduled-messages--Listar mensagens agendadas (status/cliente/setor)/v1/scheduled-messages--Agendar mensagem 1:1 de texto (send_at futuro, quota do plano)/v1/scheduled-messages/:id--Detalhe da mensagem agendada/v1/scheduled-messages/:id--Cancelar (soft; 409 se já enviada)Tags
8 endpoints/v1/tags--Listar etiquetas da empresa/v1/tags--Criar etiqueta (nome único; 409 se duplicado)/v1/tags/:id--Editar nome/descrição/cor/v1/tags/:id--Remover etiqueta (+ vínculos e regras)/v1/tag-rules--Listar regras de auto-tag/v1/tag-rules--Criar regra (contains/starts_with/equals/regex; cliente/conversa)/v1/tag-rules/:id--Editar regra (match_value/enabled/scope)/v1/tag-rules/:id--Remover regraContracts
7 endpoints/v1/contracts--Listar contratos (status/cliente/oportunidade/busca)/v1/contracts--Criar rascunho (envie depois com POST /v1/contracts/:id/send)/v1/contracts/:id--Detalhe (sanitizado, sem PDF/token do provedor)/v1/contracts/:id--Editar rascunho (title/value; 409 se não-rascunho)/v1/contracts/:id--Cancelar contrato/v1/contracts/:id/send--Enviar para assinatura (PDF + link pelo WhatsApp da empresa; síncrono ~30-50s; exige x-actor-user-id)/v1/contract-templates--Modelos ativos (para template_id)Context
2 endpoints/v1/billing/balance--Saldos avulsos (liza/envios/leads) para alerta de recarga/v1/quick-replies--Respostas prontas empresa/setor (tom de voz para o bot)Operations
4 endpoints/v1/teams--Times + membros (para escolher team_id no handoff)/v1/agents--Atendentes atribuíveis (agent_id do handoff)/v1/queue--Fila: tickets aguardando humano (waiting_since, wait_seconds)/v1/setores--Listar caixas/números da empresa — discovery do setor_id exigido pelas outras rotasEnvio de Mensagem Única
O endpoint POST /v1/messages permite enviar uma única mensagem WhatsApp de forma sincrona. Ideal para boas-vindas, notificações de status, lembretes de agendamento e qualquer comúnicacao 1-para-1 disparada por evento.
Casos de uso tipicos
- Mensagem de boas-vindas após cadastro
- Lembrete de agendamento ou consulta
- Notificacao de status (pedido enviado, pagamento confirmado)
- Confirmação de código ou link de acesso
Diferenca de Campanhas em Massa
- Síncrono - a resposta já confirma se a mensagem foi enviada
- Sem delays anti-ban - envio imediato, sem fila de campanha
- Sem tracking de campanha - não cria campanha nem gera relatórios de lote
- Para envios em massa (>1 destinatario), use
POST /v1/campaigns
Request
/v1/messagesScope necessário: messages:write
Headers
| Header | Valor |
|---|---|
| Authorization | Bearer YOUR_API_KEY |
| Content-Type | application/json |
Body
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| phone | string | Obrigatório* | Numero do destinatario com DDI (ex: +5511999999999). Use phone ou client_id. Se ambos forem enviados, client_id tem prioridade. |
| client_id | string | Obrigatório* | ID de um contato existente na base. Use phone ou client_id. |
| setor_id | string | Não | Opcional. Se omitido, o sistema seleciona automaticamente um número WhatsApp conectado da sua empresa. |
| message | object | Sim | Conteudo da mensagem (text, media ou template) |
| message.text | string | Condicional | Texto da mensagem (obrigatório se não enviar media/template) |
| message.media_url | string | Não | URL pública da mídia (HTTPS) |
| message.media_type | string | Condicional | Tipo da mídia: image, video, audio, document. Obrigatório se media_url informado. |
| message.template_name | string | Nao** | Nome do template aprovado no Meta Business (para Meta Cloud API) |
| message.template_language | string | Nao** | Código do idioma (ex: pt_BR) |
| message.template_components | array | Nao | Componentes do template Meta (header, body, buttons) conforme especificacao da Cloud API |
* Informe phone ou client_id — pelo menos um e obrigatório.
** Obrigatório apenas para envio de templates Meta. Para mensagens de texto ou mídia, esses campos devem ser omitidos.
Exemplo - Mensagem de Texto
{
"phone": "+5511999999999",
"setor_id": "set_abc123",
"message": {
"text": "Ola Maria! Seu agendamento para amanha as 14h esta confirmado."
}
}Exemplo - Mensagem com Midia
{
"phone": "+5511999999999",
"message": {
"media_url": "https://cdn.example.com/comprovante.pdf",
"media_type": "document"
}
}Exemplo - Template (Meta Cloud API)
{
"phone": "+5511999999999",
"setor_id": "set_abc123",
"message": {
"template_name": "appointment_reminder",
"template_language": "pt_BR",
"template_components": [
{ "type": "body", "parameters": [{ "type": "text", "text": "Maria" }, { "type": "text", "text": "15/03/2026" }, { "type": "text", "text": "14:00" }] }
]
}
}Response - 201 Created
{
"success": true,
"data": {
"message_id": "msg_a1b2c3d4e5f6",
"phone": "+5511999999999",
"status": "sent",
"timestamp": "2026-03-15T14:30:00.000Z"
}
}Erros
| Status | Código | Descrição |
|---|---|---|
| 400 | VALIDATION_ERROR | Corpo inválido - campo obrigatório ausente ou formato incorreto |
| 401 | UNAUTHORIZED | API key ausente ou invalida |
| 403 | FORBIDDEN | Scope messages:write ausente ou limite diario excedido |
| 404 | NOT_FOUND | Setor não encontrado ou sem instancia WhatsApp conectada |
| 500 | INTERNAL_ERROR | Erro interno ao tentar enviar a mensagem |
cURL
curl -X POST https://api-worker.agilizai.tech/v1/messages \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone": "+5511999999999",
"setor_id": "set_abc123",
"message": {
"text": "Ola Maria! Seu agendamento para amanha as 14h esta confirmado."
}
}'+5511987654321, 5511987654321, +55 (11) 98765-4321.Para envios em massa com mais de 1 destinatario, use POST /v1/campaigns que inclui delays anti-ban, agendamento e acompanhamento por dashboard/webhooks.
Primeiro Contato
O endpoint POST /v1/messages/first-contact e ideal para integracoes com CRMs, formularios e landing pages. Ele envia a mensagem, cria o contato automaticamente se não existir, estabelece afinidade com o numero remetente e permite aplicar tags ao contato.
Casos de uso
- Lead capturado em formulario do site
- Novo cadastro em landing page de campanha
- Integração com CRM após qualificação de lead
- Boas-vindas automáticas após opt-in
Request
/v1/messages/first-contactScope necessário: messages:write
Body
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| phone | string | Sim | Numero do destinatario com DDI (ex: +5511999999999) |
| name | string | Nao | Nome do contato. Usado na criação automatica do registro |
| message | string | Condicional | Texto da mensagem. Obrigatório se media_url não informado |
| media_url | string | Condicional | URL pública da mídia (HTTPS). Obrigatório se message omitido |
| media_type | string | Condicional | Tipo da mídia: image, video, audio, document. Obrigatório com media_url |
| setor_id | string | Nao | Se omitido, usa afinidade existente ou auto-seleciona |
| tags | string[] | Nao | IDs de tags para aplicar ao contato automaticamente |
Exemplo
{
"phone": "+5511999990001",
"name": "Maria Silva",
"message": "Ola Maria! Vi que você se cadastrou no nosso site. Como posso ajudar?",
"setor_id": "set_abc123",
"tags": ["tag_leads_site", "tag_interesse_premium"]
}Response - 201 Created
{
"success": true,
"data": {
"message_id": "msg_a1b2c3d4e5f6",
"client_id": "cli_xyz789",
"sender_number": "+5551999001234",
"sender_setor_id": "set_abc123",
"affinity_created": true,
"affinity_source": "first_contact"
}
}Diferenca do POST /v1/messages
| Aspecto | /v1/messages | /v1/messages/first-contact |
|---|---|---|
| Criacao de contato | Sim (se phone novo) | Sim (sempre, com name opcional) |
| Afinidade | Upsert silencioso | Upsert com source tracking |
| Tags | Não suporta | Suporta array de tag IDs |
| Input por client_id | Sim | Não (sempre por phone) |
| Resposta | message_id, status | + client_id, affinity_created |
Mensagens Interativas
O endpoint POST /v1/messages/interactive permite enviar mensagens com botões clicáveis ou listas de opções nativas do WhatsApp. Ideal para qualificação de leads, pesquisas rapidas e menus de atendimento.
Request
/v1/messages/interactiveScope necessário: messages:write
Botoes (button)
Até 3 botões clicáveis. Título máximo de 20 caracteres por botão.
Lista (list)
Até 10 opções em formato lista. Título máximo de 24 caracteres por opção.
| Tipo | Max itens | Max caracteres título |
|---|---|---|
| button | 3 botões | 20 |
| list | 10 rows | 24 |
Exemplo - Botoes
{
"phone": "+5511999990001",
"interactive": {
"type": "button",
"body": { "text": "Qual horário prefere para a visita?" },
"action": {
"buttons": [
{ "type": "reply", "reply": { "id": "btn_manha", "title": "Manha (9h-12h)" } },
{ "type": "reply", "reply": { "id": "btn_tarde", "title": "Tarde (14h-18h)" } },
{ "type": "reply", "reply": { "id": "btn_noite", "title": "Noite (19h-21h)" } }
]
}
}
}Exemplo - Lista
{
"phone": "+5511999990001",
"interactive": {
"type": "list",
"body": { "text": "Selecione o servico desejado:" },
"action": {
"sections": [
{
"rows": [
{ "id": "srv_1", "title": "Consultoria", "description": "Sessao de 1 hora" },
{ "id": "srv_2", "title": "Implementacao", "description": "Setup completo" },
{ "id": "srv_3", "title": "Suporte", "description": "Atendimento tecnico" }
]
}
]
}
}
}Response - 201 Created
{
"success": true,
"data": {
"message_id": "msg_a1b2c3d4e5f6",
"status": "sent",
"phone": "+5511999990001",
"interactive_type": "button",
"timestamp": "2026-03-15T14:30:00.000Z"
}
}Fallback automatico para texto
Quando o canal WhatsApp não suporta mensagens interativas, o sistema envia automaticamente uma versao em texto numerado. Exemplo para botões:
Qual horário prefere para a visita?
1. Manha (9h-12h)
2. Tarde (14h-18h)
3. Noite (19h-21h)Evento de Calendario
O endpoint POST /v1/messages/event envia um card de evento nativo do WhatsApp que sincroniza com o calendario do celular do destinatario. Disponivel em alguns canais WhatsApp; nos demais, o sistema envia automaticamente uma versao em texto formatado.
Request
/v1/messages/eventScope necessário: messages:write
Body
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| phone | string | Sim | Numero do destinatario com DDI |
| event.name | string | Sim | Título do evento |
| event.startTime | number | Sim | Unix timestamp em segundos. Deve ser no futuro |
| event.endTime | number | Nao | Unix timestamp. Deve ser maior que startTime |
| event.description | string | Nao | Descrição do evento |
| event.location | string | Nao | Local do evento |
| event.extraGuestsAllowed | boolean | Nao | Permitir convidados extras |
Exemplo
{
"phone": "+5511999990001",
"event": {
"name": "Reuniao de Alinhamento",
"description": "Revisao do projeto Q2",
"startTime": 1741017600,
"endTime": 1741021200,
"location": "Sala 3, Escritorio Central"
}
}Response - 201 Created
{
"success": true,
"data": {
"message_id": "msg_a1b2c3d4e5f6",
"status": "sent",
"phone": "+5511999990001",
"event_name": "Reuniao de Alinhamento",
"start_time": 1741017600,
"end_time": 1741021200,
"timestamp": "2026-03-15T14:30:00.000Z"
}
}Enquete (Poll)
O endpoint POST /v1/messages/poll envia uma enquete nativa do WhatsApp com opções clicáveis. Ideal para pesquisas de satisfacao, qualificação e votacoes rapidas.
Request
/v1/messages/pollScope necessário: messages:write
Body
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| phone | string | Sim | Numero do destinatario com DDI |
| poll.name | string | Sim | Pergunta da enquete |
| poll.options | string[] | Sim | Lista de opções clicáveis. Mínimo 2, máximo 12 |
| poll.multipleAnswers | boolean | Nao | Permitir selecao de multiplas respostas (default: false) |
Exemplo
{
"phone": "+5511999990001",
"poll": {
"name": "Qual o melhor dia para a entrega?",
"options": ["Segunda", "Terca", "Quarta", "Quinta", "Sexta"],
"multipleAnswers": true
}
}Response - 201 Created
{
"success": true,
"data": {
"message_id": "msg_a1b2c3d4e5f6",
"status": "sent",
"phone": "+5511999990001",
"poll_name": "Qual o melhor dia para a entrega?",
"options_count": 5,
"timestamp": "2026-03-15T14:30:00.000Z"
}
}Criar Campanha - Referência Completa
Documentacao detalhada de todos os campos aceitos no endpoint POST /v1/campaigns.
POST /v1/campaigns HTTP/1.1
Host: api-worker.agilizai.tech
Authorization: Bearer ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
{
"name": "Promocao de Verao",
"setor_id": "set_abc123",
"recipients": [
{
"phone": "+5511999990001",
"name": "Maria Silva"
},
{
"client_id": "cli_def456"
}
],
"message": {
"text": "{{saudacao}}, {{primeiro_nome}}! Confira nossa promocao especial.",
"media_url": "https://cdn.exemplo.com/promo-verao.png",
"media_type": "image",
"media_delivery": "inline"
},
"options": {
"schedule_at": "2026-03-10T10:00:00Z",
"anti_ban_level": "seguro",
"callback_url": "https://meusite.com/webhook"
}
}Campos principais
namestringNome identificador da campanha. Aparece no historico e webhooks.
"Promocao de Verao"setor_idstringOpcional. Numero único para envio. Se omitido, auto-seleciona.
"set_abc123"setor_idsstring[]Opcional. Lista de números para distribuição multi-numero. Alternativa a setor_id.
["set_abc123", "set_def456"]distribution_modestringOpcional. "single" (um numero) ou "multi_number" (distribuição). Inferido automaticamente se omitido.
"multi_number"multi_numberbooleanOpcional. Se true, auto-resolve todos os números elegíveis para distribuição.
truerecipientsarrayLista de destinatarios (min 1, max 10.000). Cada item deve ter phone ou client_id.
[{ "phone": "+5511999990001", "name": "Maria" }]message.textstringTexto da mensagem. Suporta variáveis com {{nome}}, {{primeiro_nome}}, etc.
"Ola {{nome}}, tudo bem?"Campos do destinatario (recipients[])
phonestringcondicionalNumero no formato E.164 com DDI. Obrigatório se client_id não for informado. Se o número não existir na base, um contato será criado automaticamente.
"+5511999990001"client_idstringcondicionalID de um contato já existente na base. Obrigatório se phone não for informado. Se informado junto com phone, o client_id tem prioridade.
"cli_def456"namestringopcionalNome do destinatario. Usado nas variáveis {{nome}} e {{primeiro_nome}}. Se omitido, usa o telefone.
"Maria Silva"Campos da mensagem (message)
textstringcondicionalTexto principal da mensagem. Obrigatório se template_name não informado. Suporta variáveis automáticas: {{nome}}, {{primeiro_nome}}, {{saudacao}}, {{hora}}, {{data}}, {{telefone}}.
media_urlstringopcionalURL pública da mídia a enviar (imagem, video, audio, documento). HTTPS obrigatório.
media_typestringcondicionalTipo da mídia. Obrigatório se media_url for informado. Valores: "image", "video", "audio", "document".
media_deliverystringopcionalComo enviar mídia com texto. "inline" envia mídia com caption (padrão para imagens). "after_text" envia texto primeiro, depois mídia separada (padrão para video/audio).
template_namestringcondicionalNome do template aprovado no Meta Business. Se informado, o campo text pode ser omitido.
template_languagestringcondicionalCódigo do idioma do template (ex: "pt_BR"). Obrigatório com template_name.
template_componentsarrayopcionalComponentes do template Meta (header, body, buttons) conforme especificacao da Cloud API.
template_name, template_language e template_components no objeto message, da mesma forma que em mensagens únicas. O campo text não e necessário quando se usa template.Opções avancadas (options)
schedule_atstring (ISO 8601)Agendar inicio da campanha. Se omitido, inicia imediatamente. Formato: "2026-03-10T10:00:00Z".
anti_ban_levelstringNivel de protecao anti-ban. Controla velocidade e delays entre mensagens.
callback_urlstringURL para receber webhook automatico ao finalizar esta campanha (alem dos webhooks globais configurados).
Niveis de anti_ban_level
normalIntervalo de 5 a 15s entre mensagens por número. Envio mais rápido — use com números já estabelecidos.
seguro(padrão)Intervalo de 20 a 40s. Recomendado para a maioria dos envios — bom equilíbrio entre velocidade e segurança.
lentoIntervalo de 45 a 90s. Cadência conservadora para listas grandes ou números em aquecimento.
muito_lentoIntervalo de 2 a 4 minutos. Máxima cautela para bases sensíveis.
ultra_lentoIntervalo de 5 a 8 minutos. Para número novo ou logo após uma restrição.
O intervalo efetivo respeita o piso de segurança do estágio de aquecimento do número. Valores legados moderate/aggressive equivalem a normal e conservative a seguro; valor inválido retorna 400 VALIDATION_ERROR.
Idempotencia
Para proteger contra campanhas duplicadas em caso de timeout ou retry de rede, inclua o header Idempotency-Key com um UUID único por requisição.
| Comportamento | Descrição |
|---|---|
| Primeira chamada | Cria a campanha normalmente e cacheia a resposta por 24 horas. |
| Chamadas seguintes | Retorna a resposta cacheada com meta.idempotent_replay: true. Nenhuma campanha duplicada e criada. |
POST /v1/campaigns HTTP/1.1
Host: api-worker.agilizai.tech
Authorization: Bearer ak_live_xxx
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/jsonDistribuição multi-numero
Campanhas podem ser distribuídas por múltiplos números WhatsApp para aumentar a capacidade e reduzir o risco de bloqueio.
| Cenario | Resultado |
|---|---|
Sem setor_id | Auto-seleciona. Se multi-número habilitado e vários números conectados, distribui automaticamente. |
setor_id (singular) | Usa esse numero. Modo single. |
setor_ids (array) | Distribui entre os números informados. |
multi_number: true | Auto-resolve todos os números elegíveis e distribui. |
A resposta inclui distribution_mode ("single" ou "multi_number") e number_pool_size indicando quantos números serão usados.
Horário seguro obrigatório
Campanhas imediatas (sem schedule_at) fora do horário 8h–21h (Brasília) são rejeitadas com 403 SAFE_HOUR_VIOLATION. A resposta traz tudo que você precisa pra agendar: o header HTTP Retry-After (em segundos) e, em details.next_allowed_at, o instante ISO 8601 UTC que pode ser copiado direto pra options.schedule_at. Os horários são configuráveis por empresa e visíveis em GET /v1/limits.
HTTP/1.1 403 Forbidden
Retry-After: 31500
Content-Type: application/json
{
"success": false,
"error": {
"code": "SAFE_HOUR_VIOLATION",
"message": "Envios em massa só são permitidos entre 08:00 e 21:00 (horário de Brasília). Agora são 22:15 (Brasília). Use `options.schedule_at` para agendar dentro da próxima janela (2026-05-27T08:00:00-03:00).",
"details": {
"safe_hour_start": 8,
"safe_hour_end": 21,
"current_hour_brasilia": 22,
"current_minute_brasilia": 15,
"current_time_brasilia": "2026-05-26T22:15:00-03:00",
"next_allowed_at": "2026-05-27T11:00:00.000Z",
"next_allowed_at_brasilia": "2026-05-27T08:00:00-03:00",
"suggested_schedule_at": "2026-05-27T11:00:00.000Z",
"retry_after_seconds": 31500,
"docs_url": "https://dashboard.agilizai.tech/api-docs#schedule_at"
}
}
}Como tratar: ao receber este erro, leia details.suggested_schedule_at e reenvie a mesma campanha com options.schedule_at = esse valor. Alternativamente, respeite o header Retry-After e tente novamente quando a janela abrir.
Limite de campanhas simultâneas
Cada empresa tem um teto de campanhas simultâneas (pendente + processando). Quando o teto é atingido, novas chamadas a POST /v1/campaigns são rejeitadas com 403 CONCURRENT_LIMIT_EXCEEDED. A resposta traz tudo que você precisa pra destravar: a lista (até 10) das campanhas que ocupam os slots em details.active_campaigns e, se alguma das ativas for agendada futura, o ISO 8601 UTC do próximo slot em details.next_slot_eta + o header HTTP Retry-After (em segundos). O limite vigente está em GET /v1/limits.
HTTP/1.1 403 Forbidden
Retry-After: 54000
Content-Type: application/json
{
"success": false,
"error": {
"code": "CONCURRENT_LIMIT_EXCEEDED",
"message": "Limite de campanhas simultâneas atingido (3). Aguarde a próxima conclusão ou a campanha agendada para 2026-05-27T08:00:00-03:00 ou conclua/cancele alguma das 3 ativas.",
"details": {
"limit": 3,
"active": 3,
"active_campaigns": [
{
"id": "cmp_abc123",
"titulo": "Black Friday — onda 1",
"status": "processando",
"scheduled_for": null,
"created_at": "2026-05-26T19:42:11.000Z"
},
{
"id": "cmp_def456",
"titulo": "Black Friday — onda 2",
"status": "pendente",
"scheduled_for": "2026-05-27T11:00:00.000Z",
"created_at": "2026-05-26T19:55:00.000Z"
},
{
"id": "cmp_ghi789",
"titulo": "Reativação inativos",
"status": "pendente",
"scheduled_for": "2026-05-28T11:00:00.000Z",
"created_at": "2026-05-26T20:10:00.000Z"
}
],
"next_slot_eta": "2026-05-27T11:00:00.000Z",
"retry_after_seconds": 54000,
"docs_url": "https://dashboard.agilizai.tech/api-docs#concurrent_limit"
}
}
}Como tratar: existem três caminhos. (1) Aguardar — respeite o header Retry-After e tente novamente quando o próximo slot abrir. (2) Liberar slot — chame POST /v1/campaigns/{id}/cancel em uma das campanhas listadas em details.active_campaigns. (3) Aumentar o teto — peça ao administrador da empresa pra ajustar envios_massa_max_concurrent no plano (campanhas agendadas para o futuro continuam contando como ativas enquanto não rodam).
Resposta (202 Accepted)
A campanha e enfileirada para processamento assíncrono. O status inicial e "pendente".
{
"success": true,
"data": {
"id": "cmp_abc123xyz",
"status": "queued",
"total_recipients": 2,
"credits_reserved": 2,
"created_at": "2026-03-10T10:00:00Z",
"execution_mode": "immediate",
"scheduled_for": null,
"waiting_reason": null,
"distribution_mode": "single",
"number_pool_size": 1
}
}pendente (aguardando processamento), processando (enviando), pausado, concluido e falha. Na resposta de criação, queued equivale a pendente.immediate (inicio imediato), scheduled (agendado via schedule_at), ou deferred_to_safe_window (adiado para o próximo horário seguro, com waiting_reason: "outside_safe_hours").Limites de envio
- Max destinatarios: 10.000 por campanha.
- Limite diario: Configurado por empresa (padrão 500/dia). Verificado ao criar campanha.
- Campanhas simultâneas: Limite configurado por empresa (padrão 1). Aguarde a atual finalizar ou veja os planos.
- Numero conectado: O setor deve ter um WhatsApp conectado (numero_contato configurado).
Personalizacao de Mensagens
Use variáveis no texto da mensagem para personalizar cada envio automaticamente. A personalizacao aumenta o engajamento e reduz o risco de bloqueio.
Variaveis automáticas
Substituidas automaticamente com dados do destinatario e contexto.
| Variavel | Valor | Exemplo |
|---|---|---|
{{nome}} | Nome completo do destinatario | Maria Silva |
{{primeiro_nome}} | Primeiro nome do destinatario | Maria |
{{telefone}} | Numero de telefone | +5511999990001 |
{{saudacao}} | Saudacao automatica por horário (Bom dia / Boa tarde / Boa noite) | Bom dia |
{{hora}} | Hora atual no formato HH:MM | 14:30 |
{{data}} | Data atual no formato DD/MM/AAAA | 06/03/2026 |
Variacao automatica (anti-spam)
O sistema aplica automaticamente duas camadas de variacao para evitar deteccao de spam pelo WhatsApp:
O sistema personaliza automaticamente as saudacoes para manter as mensagens naturais e únicas.
Variacao visivel: A variável {{saudacao}} alterna automaticamente entre formas como "Bom dia", "Ola, bom dia" e "Oi! Bom dia" para parecer mais natural.
Essas variacoes sao aplicadas automaticamente. Você não precisa fazer nada — apenas use as variáveis normalmente.
Ciclo de Vida da Campanha
Uma campanha passa por diferentes status durante sua execução. Você pode pausar, retomar ou cancelar em qualquer momento.
Fluxo de status
POST /v1/campaigns/:id/pause-- Pausar campanha
Pausa uma campanha em andamento (status "processando"). As mensagens ja enviadas não sao afetadas. Mensagens pendentes ficam em espera ate você retomar.
POST /v1/campaigns/cmp_abc123/pause
// Resposta:
{
"success": true,
"data": { "status": "paused" }
}
// Webhook disparado: campaign.pausedPOST /v1/campaigns/:id/resume-- Retomar campanha
Retoma uma campanha pausada. A campanha e re-enfileirada e continua enviando apenas as mensagens que ainda estao pendentes. Mensagens já enviadas ou com falha não sao reprocessadas.
POST /v1/campaigns/cmp_abc123/resume
// Resposta:
{
"success": true,
"data": {
"status": "processing",
"pending_requeued": 158
}
}POST /v1/campaigns/:id/cancel-- Cancelar campanha
Cancela uma campanha em status "processando" ou "pausado". Todas as mensagens pendentes sao marcadas como "pulado" (skipped) com motivo "cancelled_by_user". Mensagens já enviadas permanecem inalteradas. A campanha muda para status "concluido".
POST /v1/campaigns/cmp_abc123/cancel
// Resposta:
{
"success": true,
"data": {
"cancelled": 158
}
}
// Webhook disparado: campaign.completed (com cancelledByUser: true)pause em vez de cancel.Acompanhamento e Historico
Construa uma tela de monitoramento em tempo real e historico completo dos envios usando os endpoints de leitura. Combine com webhooks para atualizacoes instantaneas.
GET /v1/campaigns-- Historico de campanhas
Lista todas as campanhas com paginação. Filtre por status para montar abas (ativas, concluidas, falhas).
GET /v1/campaigns?page=1&limit=20&status=concluido
// Resposta:
{
"success": true,
"data": {
"items": [
{
"id": "cmp_abc123",
"mensagem": "Ola {{nome}}! Confira nossa promocao.",
"total_destinatarios": 500,
"total_enviados": 487,
"total_falhas": 8,
"total_pulados": 5,
"status": "concluido",
"created_at": "2026-03-05T10:00:00Z"
},
{
"id": "cmp_def456",
"mensagem": "Lembrete: seu agendamento amanha.",
"total_destinatarios": 120,
"total_enviados": 120,
"total_falhas": 0,
"total_pulados": 0,
"status": "concluido",
"created_at": "2026-03-04T14:30:00Z"
}
],
"total": 47,
"page": 1,
"limit": 20,
"totalPages": 3
}
}status (pendente, processando, concluido, falha, pausado), page, limit (max 100).GET /v1/campaigns/:id-- Progresso em tempo real
Retorna os contadores atualizados e o percentual de progresso. Use polling (5-10s) para atualizar uma barra de progresso.
GET /v1/campaigns/cmp_abc123
// Resposta:
{
"success": true,
"data": {
"id": "cmp_abc123",
"mensagem": "Ola {{nome}}! Confira nossa promocao.",
"imagem_url": null,
"total_destinatarios": 500,
"total_enviados": 342,
"total_falhas": 5,
"total_pulados": 3,
"status": "processando",
"progress": 70,
"created_at": "2026-03-05T10:00:00Z",
"updated_at": "2026-03-05T10:15:32Z"
}
}progress e calculado como (enviados + falhas + pulados) / total * 100. Quando chegar a 100%, o status será "concluido".GET /v1/campaigns/:id/messages-- Detalhes por destinatario
Lista o status de cada mensagem individual. Use para construir uma tabela de detalhes com nome, telefone, status e erro (se houver).
GET /v1/campaigns/cmp_abc123/messages?page=1&limit=50&status=falha
// Resposta:
{
"success": true,
"data": {
"items": [
{
"id": "det_001",
"status": "falha",
"erro": "HTTP 503: Service Unavailable",
"erro_codigo": "TEMP_SERVER_ERROR",
"erro_legivel": "Serviço temporariamente indisponível",
"created_at": "2026-03-05T10:00:00Z",
"sent_at": null,
"assigned_setor_id": "set_abc123",
"actual_setor_id": null,
"clientes": {
"nome": "Joao Silva",
"telefone": "+5511999990001"
}
},
{
"id": "det_002",
"status": "falha",
"erro": "warming_daily_limit",
"erro_codigo": "WARMING_DAILY_LIMIT",
"erro_legivel": "Limite de aquecimento do número WhatsApp atingido para hoje.",
"created_at": "2026-03-05T10:00:00Z",
"sent_at": null,
"assigned_setor_id": "set_abc123",
"actual_setor_id": null,
"clientes": {
"nome": "Maria Santos",
"telefone": "+5521888880002"
}
}
],
"total": 5,
"page": 1,
"limit": 50,
"totalPages": 1
}
}pendente, enviado, falha, pulado. Sem filtro retorna todos. Paginacao ate 200 por pagina.Como construir uma tela de acompanhamento
Listagem (historico): Use GET /v1/campaigns com filtros de status para criar abas: Ativas, Concluidas, Falhas.
Progresso (polling): Ao clicar numa campanha ativa, faca polling em GET /v1/campaigns/:id a cada 5-10 segundos para atualizar a barra de progresso e contadores.
Detalhes (tabela): Use GET /v1/campaigns/:id/messages para listar cada destinatario com nome, telefone, status e motivo de erro. Filtre por status para ver apenas falhas ou pendentes.
Tempo real (webhooks): Em vez de polling, configure um webhook para os eventos message.sent, message.failed e campaign.completed para receber atualizacoes instantaneas e atualizar a UI via WebSocket.
Exemplo: Polling de progresso (Node.js)
async function pollCampaignProgress(campaignId, apiKey) {
const BASE = 'https://api-worker.agilizai.tech';
while (true) {
try {
const res = await fetch(`${BASE}/v1/campaigns/${campaignId}`, {
headers: { 'Authorization': `Bearer ${apiKey}` },
});
const { data: campaign } = await res.json();
console.log(
`Progresso: ${campaign.progress}% | ` +
`Enviados: ${campaign.total_enviados}/${campaign.total_destinatarios} | ` +
`Falhas: ${campaign.total_falhas} | Status: ${campaign.status}`
);
// Parar quando concluido ou falhou (cancelamento também gera status 'concluido')
if (['concluido', 'falha'].includes(campaign.status)) {
console.log('Campanha finalizada!');
// Buscar mensagens que falharam
if (campaign.total_falhas > 0) {
const failedRes = await fetch(
`${BASE}/v1/campaigns/${campaignId}/messages?status=falha`,
{ headers: { 'Authorization': `Bearer ${apiKey}` } }
);
const failedPayload = await failedRes.json();
const items = failedPayload.data.items;
console.log('Falhas:', items.map(m =>
`${m.clientes.telefone}: ${m.erro}`
));
}
break;
}
} catch (error) {
console.error('Erro ao consultar campanha:', error.message);
}
await new Promise(r => setTimeout(r, 5000)); // 5s entre cada poll
}
}Conexão WhatsApp via API
Para enviar mensagens, você precisa conectar um número WhatsApp. O fluxo completo pode ser feito via API, sem precisar acessar o dashboard.
1.Criar instancia
Crie a conexão do canal para o setor. O canal e resolvido automaticamente pela configuração da empresa e o proxy e configurado automaticamente quando necessário.
POST /v1/whatsapp/instances
{
"setor_id": "set_abc123"
}
// Resposta 201:
{
"success": true,
"data": {
"instance_id": "set_abc123",
"status": "disconnected",
"proxy_configured": true,
"reused": false
}
}2.Obter QR Code
Obtenha o QR code e exiba-o no seu sistema para o usuario escanear com o WhatsApp.
GET /v1/whatsapp/instances/set_abc123/qr
// Resposta:
{
"success": true,
"data": {
"qr_code": "data:image/png;base64,iVBOR...",
"expires_in": 60
}
}O campo expires_in indica o tempo em segundos. O QR code expira em aproximadamente 60 segundos. Solicite um novo se expirar.
3.Aguardar conexão (polling)
Faca polling no endpoint de status ate o número estar conectado. Intervalo recomendado: 3-5 segundos.
GET /v1/whatsapp/instances/set_abc123/status
// Resposta durante escaneamento:
{ "success": true, "data": { "status": "connecting", "phone_number": null } }
// Resposta após conectar:
{ "success": true, "data": { "status": "connected", "phone_number": "+5511999990001" } }4.Pronto para disparar
Com status "connected", o setor esta pronto para criar campanhas.
POST /v1/campaigns
{
"name": "Primeira campanha",
"setor_id": "set_abc123", // mesmo setor conectado
"recipients": [...],
"message": { "text": "Ola {{nome}}!" }
}QR Code expira rapidamente
O QR code expira em ~60 segundos. Se expirar, faça uma nova requisição para /qr. Para desconectar, use POST /v1/whatsapp/instances/:id/disconnect.
GET /v1/whatsapp/instances/health-- Saude das instancias
Retorna o status de saude de todas as instancias WhatsApp da empresa. Use para verificar quais números estao conectados antes de criar campanhas.
GET /v1/whatsapp/instances/health?include_history=false
// Resposta:
{
"success": true,
"data": {
"items": [
{
"setor_id": "set_abc123",
"setor_nome": "Vendas",
"phone_number": "+5511999990001",
"health": {
"state": "healthy",
"connected": true,
"responding": true,
"lastCheckedAt": "2026-03-06T15:00:00Z",
"lastOkAt": "2026-03-06T15:00:00Z",
"consecutiveFailures": 0,
"error": null
}
},
{
"setor_id": "set_def456",
"setor_nome": "Suporte",
"phone_number": null,
"health": {
"state": "disconnected",
"connected": false,
"responding": false,
"lastCheckedAt": "2026-03-06T15:00:00Z",
"lastOkAt": null,
"consecutiveFailures": 5,
"error": "Instance not connected"
}
}
]
}
}Query params opcionais: include_history (boolean, default false) e history_minutes (1-1440, default 60).
Limites e cobrança
Consulte os limites efetivos da empresa, a capacidade disponível no dia e o saldo avulso atualmente disponível.
O que o endpoint informa
Habilitação: Informa se o envio em massa está ativo para a empresa e qual modalidade está em vigor.
Capacidade: Mostra limite diário, campanhas simultâneas ativas e RPM efetivo da chave.
Saldo avulso: Quando a empresa opera em modalidade avulsa, o saldo disponível aparece em `avulso_balance`.
GET /v1/limits-- Limites e capacidade
Retorna os limites efetivos da empresa, capacidade restante do dia, concorrencia ativa e RPM efetivo da chave.
GET /v1/limits
// Resposta:
{
"success": true,
"data": {
"bulk_messaging_enabled": true,
"modality": "mensal",
"plan_code": "crm-pro",
"expires_at": null,
"avulso_balance": 0,
"daily": {
"limit": 1000,
"sent_today": 240,
"remaining": 760
},
"concurrent_campaigns": {
"limit": 3,
"active": 1,
"remaining": 2
},
"max_recipients_per_campaign": 10000,
"rate_limit": {
"api_key_rpm": 60,
"company_rpm": 60,
"effective_rpm": 60
},
"safe_hours": {
"start": 8,
"end": 21
}
}
}avulso_balance -- Saldo restante de mensagens pre-pagas (modelo avulso). Cada mensagem enviada desconta 1 unidade. No modelo mensal, este valor e sempre 0.
safe_hours -- Horario permitido para envio de campanhas (fuso BRT). Campanhas agendadas fora desse horário serão iniciadas automaticamente no próximo horário permitido. Exemplo: start: 8, end: 21 significa que envios ocorrem apenas entre 8h e 21h.
Webhooks
Receba notificações em tempo real quando eventos ocorrerem na sua conta. Todos os payloads sao assinados com HMAC-SHA256 para garantir autenticidade.
Para confirmar que uma requisição realmente veio da Agilizai (e não de terceiros), cada POST inclui uma assinatura digital no header. Você pode usar o secret recebido na criação do webhook para validar essa assinatura. Essa verificação e opcional mas recomendada em producao.
Verificacao de assinatura
Cada requisição de webhook inclui 3 headers de segurança. Sempre verifique a assinatura antes de processar o evento.
X-Webhook-Id -- ID único da entrega (para deduplicacao).
X-Webhook-Timestamp -- Timestamp Unix (segundos) de quando o webhook foi enviado.
X-Webhook-Signature -- HMAC-SHA256 de {timestamp}.{payload} usando seu webhook secret.
Exemplo de verificação (Node.js / Express)
const crypto = require('crypto');
function verifyWebhookSignature(signedPayload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(signedPayload, 'utf8')
.digest('hex');
if (Buffer.from(signature).length !== Buffer.from(expected).length) {
return false;
}
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
// Express middleware example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const webhookId = req.headers['x-webhook-id']; // ID único da entrega
const timestamp = req.headers['x-webhook-timestamp']; // Unix timestamp (segundos)
const signature = req.headers['x-webhook-signature']; // HMAC-SHA256
const payload = req.body.toString();
const signedPayload = `${timestamp}.${payload}`;
if (!verifyWebhookSignature(signedPayload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Opcional: rejeitar webhooks com timestamp muito antigo (replay attack)
const age = Math.floor(Date.now() / 1000) - parseInt(timestamp);
if (age > 300) { // 5 minutos
return res.status(401).json({ error: 'Timestamp too old' });
}
const event = JSON.parse(payload);
console.log('Webhook event:', event.event, 'ID:', webhookId);
switch (event.event) {
case 'campaign.completed':
// Campanha finalizada
break;
case 'message.sent':
// Mensagem enviada com sucesso
break;
case 'message.failed':
// Falha ao enviar mensagem
break;
}
res.status(200).json({ received: true });
});Exemplo de verificação (Python / Flask)
import hmac
import hashlib
import time
from flask import Flask, request, jsonify
app = Flask(__name__)
WEBHOOK_SECRET = "seu_webhook_secret"
def verify_signature(payload: bytes, timestamp: str, signature: str) -> bool:
signed_payload = f"{timestamp}.{payload.decode('utf-8')}"
expected = hmac.new(
WEBHOOK_SECRET.encode(),
signed_payload.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
@app.route("/webhook", methods=["POST"])
def webhook():
webhook_id = request.headers.get("X-Webhook-Id")
timestamp = request.headers.get("X-Webhook-Timestamp", "")
signature = request.headers.get("X-Webhook-Signature", "")
if not verify_signature(request.data, timestamp, signature):
return jsonify({"error": "Invalid signature"}), 401
# Rejeitar timestamps antigos (> 5 min)
age = int(time.time()) - int(timestamp)
if age > 300:
return jsonify({"error": "Timestamp too old"}), 401
event = request.json
print(f"Webhook {webhook_id}: {event['event']}")
return jsonify({"received": True}), 200Referência de respostas dos endpoints de webhook
POST /v1/webhooks — 201 Created
{
"success": true,
"data": {
"id": "wh_abc123",
"url": "https://meusite.com/webhook",
"events": ["campaign.completed", "message.failed"],
"secret": "a1b2c3...hex64chars...",
"active": true,
"created_at": "2026-03-06T10:00:00Z"
}
}O campo secret e retornado apenas na criação. Armazene-o em local seguro.
GET /v1/webhooks — 200 OK
{
"success": true,
"data": {
"items": [
{
"id": "wh_abc123",
"url": "https://meusite.com/webhook",
"events": ["campaign.completed", "message.failed"],
"active": true,
"created_at": "2026-03-06T10:00:00Z",
"updated_at": "2026-03-06T12:00:00Z"
}
],
"total": 1,
"page": 1,
"limit": 1,
"totalPages": 1
}
}O campo secret não e exposto na listagem.
PUT /v1/webhooks/:id — 200 OK
Todos os campos sao opcionais: url, events, active.
{
"success": true,
"data": {
"id": "wh_abc123",
"url": "https://meusite.com/webhook-v2",
"events": ["campaign.completed"],
"active": true,
"updated_at": "2026-03-06T14:00:00Z"
}
}DELETE /v1/webhooks/:id — 200 OK
{
"success": true,
"data": {
"deleted": true,
"id": "wh_abc123"
}
}Eventos disponíveis
campaign.startedEnvio iniciado
campaign.completedTodos os envios finalizados
campaign.failedCampanha falhou
campaign.pausedCampanha pausada
message.sentMensagem enviada com sucesso
message.failedFalha ao enviar mensagem
Formato do payload
Todos os webhooks seguem a mesma estrutura. O campo data varia conforme o evento.
campaign.completed
{
"event": "campaign.completed",
"timestamp": "2026-03-06T15:30:00.000Z",
"data": {
"campaignId": "cmp_abc123",
"totalDestinatarios": 500,
"totalEnviados": 487,
"totalFalhas": 8,
"totalPulados": 5,
"cancelledByUser": false
}
}cancelledByUser— true quando a campanha foi cancelada manualmente, false quando concluiu normalmente
message.sent
{
"event": "message.sent",
"timestamp": "2026-03-06T15:25:12.000Z",
"data": {
"campaignId": "cmp_abc123",
"detailId": "det_001",
"recipientPhone": "+5511999990001",
"recipientName": "Maria Silva",
"status": "sent",
"error": null,
"error_code": null,
"actual_setor_id": "set_abc123",
"was_reassigned": false
}
}detailId— ID único desta mensagem na campanhaerror_code— código do erro (ex: SENDER_DISCONNECTED, RECIPIENT_NOT_FOUND). Null quando enviado com sucessoactual_setor_id— qual número WhatsApp enviou a mensagem (pode diferir se houver redistribuição automatica)was_reassigned— indica se a mensagem foi redirecionada para outro número automaticamente
message.failed
{
"event": "message.failed",
"timestamp": "2026-03-06T15:25:45.000Z",
"data": {
"campaignId": "cmp_abc123",
"detailId": "det_042",
"recipientPhone": "+5521888880002",
"recipientName": "Joao Santos",
"status": "failed",
"error": "Numero não encontrado no WhatsApp",
"error_code": "RECIPIENT_NOT_FOUND",
"actual_setor_id": null,
"was_reassigned": false
}
}detailId— ID único desta mensagem na campanhaerror_code— código do erro (ex: SENDER_DISCONNECTED, RECIPIENT_NOT_FOUND). Null quando enviado com sucessoactual_setor_id— qual número WhatsApp enviou a mensagem (pode diferir se houver redistribuição automatica)was_reassigned— indica se a mensagem foi redirecionada para outro número automaticamente
campaign.started
{
"event": "campaign.started",
"timestamp": "2026-03-06T15:00:00.000Z",
"data": {
"campaignId": "cmp_abc123",
"setorId": "set_abc123",
"userId": "user_xyz789"
}
}campaign.paused
{
"event": "campaign.paused",
"timestamp": "2026-03-06T15:20:00.000Z",
"data": {
"campaignId": "cmp_abc123"
}
}campaign.failed
{
"event": "campaign.failed",
"timestamp": "2026-03-06T15:30:00.000Z",
"data": {
"campaignId": "cmp_abc123",
"error": "Instancia WhatsApp desconectada"
}
}Politica de retry
Se seu endpoint retornar erro (5xx ou timeout), o webhook será reenviado automaticamente com backoff exponencial:
Apos 5 tentativas sem sucesso, o webhook e marcado como falho. Erros 4xx (exceto 429) não sao retentados. Seu endpoint deve responder com 2xx em ate 5 segundos.
O endpoint POST /v1/webhooks/:id/test também aplica timeout de 5 segundos. Se seu endpoint não responder dentro desse prazo, o teste será considerado falho (abort).
Códigos de Erro
A API utiliza códigos HTTP padrão. Respostas de erro seguem o envelope JSON do contrato público, com success: false e um objeto error contendo código e descrição legivel.
{
"success": false,
"error": {
"code": "BAD_REQUEST",
"message": "name, setor_id, and recipients[] are required"
}
}| Código | Nome | Descrição |
|---|---|---|
| 400 | Bad Request | A requisição contém dados inválidos. Verifique o corpo da requisição. |
| 401 | Unauthorized | Token de API ausente ou inválido. Verifique o header Authorization. |
| 403 | Forbidden | Sua API key não possui o scope necessário para este endpoint. |
| 404 | Not Found | O recurso solicitado não existe ou foi removido. |
| 429 | Too Many Requests | Limite de requisicoes excedido. Aguarde antes de tentar novamente. |
| 500 | Internal Server Error | Erro interno do servidor. Tente novamente ou entre em contato com o suporte. |
Códigos de erro classificados
Códigos retornados no campo erro_codigo nas mensagens de campanha e no campo error_code nos webhooks de falha.
Erros do remetente
| Código | Descrição |
|---|---|
SENDER_BANNED | Número de envio bloqueado pelo WhatsApp |
SENDER_CORRUPT | Sessão do número corrompida |
SENDER_REPLACED | Sessão do número substituída por outro dispositivo |
SENDER_RESTRICTED | Número de envio com restrições |
SENDER_DISCONNECTED | WhatsApp do número de envio está offline |
SENDER_AUTH | Número de envio sem autorização |
Erros do destinatario
| Código | Descrição |
|---|---|
RECIP_NOT_WHATSAPP | Destinatário não tem WhatsApp |
RECIP_BLOCKED | Destinatário bloqueou este número |
RECIP_INVALID | Número do destinatário inválido |
META_WINDOW_EXPIRED | Janela de 24h expirada para este destinatário |
META_NOT_ELIGIBLE | Destinatário não pode receber mensagens |
Erros temporarios
| Código | Descrição |
|---|---|
TEMP_RATE_LIMIT | Limite de velocidade atingido |
TEMP_TIMEOUT | Tempo de resposta excedido |
TEMP_NETWORK | Erro de conexão temporário |
TEMP_SERVER_ERROR | Serviço temporariamente indisponível |
TEMP_RECONNECTING | Reconectando o número |
Outros
| Código | Descrição |
|---|---|
UNKNOWN | Erro não identificado no envio |
Planos e Limites (Envio em Massa)
O envio em massa é habilitado por empresa e opera por modalidade mensal ou avulso. O endpoint GET /v1/limits é a fonte de verdade para o cliente externo saber se pode enviar e quais limites aplicar.
| Modalidade | Comportamento | Regra de bloqueio |
|---|---|---|
| mensal | Envio ativo enquanto a empresa estiver habilitada no admin. | Bloqueia quando estoura limite diário, concorrência ativa ou RPM. |
| avulso | Envio ativo até expires_at. | Se expirar, novas campanhas retornam 403. |
| inativo | Recurso desabilitado para a empresa. | Criação de campanha bloqueada com 403. |
daily, concurrent_campaigns, rate_limit, safe_hours).Limites e Rate Limits
A API possui dois tipos de limites: limites de requisicoes (rate limit) e limites de envio (volume de mensagens). Ambos sao configurados individualmente por empresa no painel administrativo.
Rate Limit (requisicoes API)
Limita o número de chamadas HTTP por minuto. Dois niveis sao aplicados simultaneamente:
Por API Key
60 req/min
Configuravel individualmente por API key. Cada API key tem seu proprio contador.
Por Empresa
60 req/min
Agregado de todas as keys da empresa. Se a empresa tiver varias keys, o limite total da empresa também se aplica.
Limites de Envio (mensagens)
Controlam o volume de mensagens que podem ser enviadas. Sao verificados automaticamente ao criar uma campanha e durante o processamento.
| Limite | Padrao | Descrição | Erro retornado |
|---|---|---|---|
Mensagens / dia | 500 | Total de mensagens enviadas com sucesso no dia. Ao criar campanha, verifica se (enviados_hoje + novos_destinatarios) excede o limite. | 403: Daily messages limit exceeded |
Campanhas simultâneas | 1 | Numero máximo de campanhas com status "pendente" ou "processando" ao mesmo tempo. Aguarde a campanha atual finalizar ou cancele antes de criar outra. | 403: Concurrent campaigns limit reached |
Destinatarios / campanha | 10.000 | Numero máximo de destinatarios em uma única campanha. Para volumes maiores, divida em multiplas campanhas. | 400: Maximum 10000 recipients |
Warming diario | 50 - 1000 | O limite por número WhatsApp e baseado no historico de uso. Numeros novos tem limites mais baixos que crescem gradualmente com o uso responsavel. | Mensagem pulada: warming_daily_limit |
GET /v1/limits-- Consultar seus limites
Retorna todos os limites configurados para sua empresa, incluindo uso atual no dia. Use este endpoint para validar antes de criar campanhas e exibir informações na sua interface.
GET /v1/limits
// Resposta:
{
"success": true,
"data": {
"bulk_messaging_enabled": true,
"modality": "mensal",
"expires_at": null,
"daily": {
"limit": 500,
"sent_today": 342,
"remaining": 158
},
"concurrent_campaigns": {
"limit": 1,
"active": 0,
"remaining": 1
},
"max_recipients_per_campaign": 10000,
"rate_limit": {
"api_key_rpm": 60,
"company_rpm": 60,
"effective_rpm": 60
},
"safe_hours": {
"start": 8,
"end": 21
}
}
}daily.remaining -- Quantas mensagens ainda podem ser enviadas hoje.
concurrent_campaigns.remaining -- Quantas campanhas podem ser criadas agora (se 0, aguarde uma finalizar).
rate_limit.effective_rpm -- O limite efetivo de requisicoes API por minuto (menor entre key e empresa).
GET /v1/limits antes de criar uma campanha para verificar se tem capacidade disponível. Se daily.remaining for menor que o número de destinatarios, a campanha será rejeitada com erro 403.Modalidade de acesso
O envio em massa precisa estar habilitado para sua empresa. Existem duas modalidades:
Plano mensal
Acesso continuo enquanto a assinatura estiver ativa. Limites definidos pelo plano contratado.
Avulso
Acesso temporario com data de expiracao. Apos expirar, novas campanhas sao bloqueadas (erro 403: "Plano avulso expirado").
Headers de Rate Limit
Cada resposta inclui headers indicando seu uso atual:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1706817600X-RateLimit-Limit -- Numero máximo de requisicoes permitidas na janela (menor entre key e empresa).
X-RateLimit-Remaining -- Requisicoes restantes na janela atual.
X-RateLimit-Reset -- Timestamp (Unix) de quando a janela será reiniciada.
Ao receber 429 Too Many Requests
A resposta informa se o limite excedido foi da key ou da empresa. Aguarde o tempo indicado no campo retryAfter (em segundos) antes de tentar novamente.
{
"success": false,
"error": {
"code": "TOO_MANY_REQUESTS",
"message": "Rate limit exceeded. Key: 60/min, Company: 30/min.",
"retryAfter": 60
}
}Exemplos de Código
Exemplo completo de criação de uma campanha de envio em massa em diferentes linguagens.
Envio de mensagem única
POST /v1/messages -- Envia uma mensagem individual de forma sincrona.
curl -X POST https://api-worker.agilizai.tech/v1/messages \
-H "Authorization: Bearer ak_live_sua_chave_aqui" \
-H "Content-Type: application/json" \
-d '{
"phone": "+5511999999999",
"message": {
"text": "Ola! Seu pedido #1234 foi confirmado."
}
}'Criar campanha
POST /v1/campaigns -- Cria uma campanha com agendamento e personalizacao automatica.
curl -X POST https://api-worker.agilizai.tech/v1/campaigns \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Promocao Janeiro",
"setor_id": "set_abc123",
"message": { "text": "Ola {{nome}}! Confira nossa promocao especial." },
"recipients": [
{ "phone": "+5511999999999", "name": "Maria" },
{ "phone": "+5521888888888", "name": "Joao" }
],
"options": {
"schedule_at": "2026-01-15T10:00:00Z",
"anti_ban_level": "seguro"
}
}'Resposta esperada
202 Accepted
{
"success": true,
"data": {
"id": "cmp_abc123xyz",
"status": "queued",
"total_recipients": 2,
"credits_reserved": 2,
"created_at": "2026-01-10T14:30:00Z",
"execution_mode": "immediate",
"scheduled_for": null,
"waiting_reason": null,
"distribution_mode": "single",
"number_pool_size": 1
}
}Pronto para integrar?
Crie sua API key no dashboard e comece a enviar campanhas programaticamente em minutos.