Documentação da API

API REST para ligar o seu sistema ao atendimento: criar agendamentos, enviar mensagens no WhatsApp, consultar contatos e conversas — e receber avisos quando algo acontece do nosso lado.

Como começar

1. Gere uma chave. No painel, entre em Integrações e clique em gerar chave. Dê um nome que diga de onde ela vem ("Site", "Sistema da clínica", "n8n"). A chave aparece uma única vez — copie na hora. Guardamos apenas o hash dela, então não há como recuperá-la depois; se perder, gere outra e revogue a antiga.

2. Anote o seu endereço base. É o mesmo domínio onde você acessa o painel, com /api/v1 no fim:

https://SEU-DOMINIO/api/v1

3. Confirme que está tudo certo:

curl https://SEU-DOMINIO/api/v1/ping \
  -H "X-API-Key: atz_sua_chave_aqui"
{ "ok": true, "ts": "2026-09-28T14:03:11.204Z" }

Todas as respostas são JSON. As listas vêm sempre dentro de data. Os horários são ISO 8601 em UTC.

Autenticação

Toda requisição precisa da chave, em um dos dois headers — tanto faz qual:

X-API-Key: atz_sua_chave_aqui
Authorization: Bearer atz_sua_chave_aqui

A chave age com as permissões do dono da conta: ela enxerga e faz tudo o que o dono faria. Trate-a como senha — não coloque em código que vai para o navegador nem em repositório público.

Cada chave registra a data do último uso, que aparece no painel. É a forma mais simples de descobrir se uma integração parou: se a data congelou, parou. Revogar é imediato — quem estiver usando aquela chave recebe 401 na requisição seguinte.

Limites e erros

SituaçãoLimite
Com chave válida300 requisições por minuto
Sem chave30 por minuto, por IP

A resposta traz os headers padrão RateLimit-* com o que resta da janela. Estourando, vem 429 e basta esperar um minuto.

CódigoO que significa
200 / 201Deu certo (201 quando algo foi criado).
400Falta um campo ou o valor não serve. A mensagem diz qual.
401Chave ausente, inválida ou revogada.
404O que você pediu não existe nesta conta.
409Existe, mas não está em estado de ser usado (ex.: modelo ainda não aprovado).
429Limite de requisições.
502Nós aceitamos, mas o WhatsApp recusou o envio. A mensagem traz o motivo.

Erros vêm sempre no mesmo formato:

{ "error": "Telefone inválido." }

Contatos

GET/v1/ping

Não faz nada e não altera nada — serve para confirmar que a chave é válida e que o endereço está certo. É por aqui que se começa a depurar quando algo não responde.

{ "ok": true, "ts": "2026-09-28T14:03:11.204Z" }
GET/v1/contacts
ParâmetroDescrição
searchProcura por nome ou telefone.
limitPadrão 50, máximo 200.
{ "data": [
  { "id": 412, "name": "Maria Souza", "phone": "5521999998888",
    "email": null, "birthday": null, "created_at": "2026-09-12 14:02:55" }
] }
POST/v1/contacts
CampoDescrição
phone obrigatórioSó dígitos, com DDI e DDD. Pontuação é ignorada.
nameSe vazio, usamos o próprio telefone como nome.
emailOpcional.

Se o telefone já existir, não duplicamos: atualizamos nome e e-mail (quando vierem preenchidos) e devolvemos "created": false. Pode chamar à vontade, é seguro repetir.

Agendamentos

GET/v1/appointments
ParâmetroDescrição
fromData/hora inicial (ISO). Sem ela, começa em ontem.
toData/hora final (ISO).
limitPadrão 100, máximo 500.

Devolve só o que está de pé — scheduled, confirmed ou done — em ordem de horário. Cancelados ficam de fora.

{ "data": [
  { "id": 903, "title": "Consulta", "starts_at": "2026-09-30T13:00:00.000Z",
    "duration_min": 30, "status": "scheduled", "resource_id": null,
    "contact_name": "Maria Souza", "contact_phone": "5521999998888" }
] }
POST/v1/appointments
CampoDescrição
contact_phone ou contact_id obrigatório Pelo telefone é o mais simples: se o contato não existir, criamos.
title obrigatórioO que vai aparecer na agenda ("Consulta", "Retorno").
starts_at obrigatórioInício, em ISO 8601 com fuso.
duration_minPadrão 30.
curl -X POST https://SEU-DOMINIO/api/v1/appointments \
  -H "X-API-Key: atz_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_phone": "5521999998888",
    "title": "Consulta",
    "starts_at": "2026-09-30T10:00:00-03:00",
    "duration_min": 30
  }'

O agendamento nasce como scheduled, aparece na agenda do painel na hora, e dispara o webhook appointment.created com "source": "api".

Atenção a quem usa etiquetas nos relatórios. Um agendamento criado por aqui entra sem etiquetas. Se a sua operação conta leads ou separa profissionais por etiqueta, decida antes quem vai colocá-las — senão o agendamento existe na agenda e some dos relatórios.

Conversas

GET/v1/conversations
ParâmetroDescrição
statusopen, waiting ou closed.
limitPadrão 50, máximo 200.
{ "data": [
  { "id": 1841, "status": "open", "updated_at": "2026-09-28 11:27:04",
    "contact_name": "Maria Souza", "contact_phone": "5521999998888",
    "attendant_name": "Raphaella" }
] }

Enviar mensagem

POST/v1/messages
CampoDescrição
phone obrigatórioDestino, só dígitos com DDI e DDD.
bodyO texto. Obrigatório, exceto se usar template_name.
line_idPor qual número enviar. Sem ele, reaproveitamos a conversa ativa do contato (e a linha dela) ou usamos a linha padrão. Um line_id que não existe dá 400 — nunca cai na padrão em silêncio.
nameNome do contato vindo do seu sistema. Prevalece sobre o nome do perfil do WhatsApp — quem dispara sabe de quem se trata.
tagsAté 5 etiquetas, criadas se não existirem: ["Vaga: Repositor"].
assign_toId ou e-mail de quem fica com a conversa. Se não corresponder a ninguém ativo, enviamos mesmo assim, com atribuição automática, e devolvemos um aviso.
pause_aiPausa a assistente nesta conversa, para ela não responder por cima de quem está assumindo. Aceita true, 1 ou "1".
curl -X POST https://SEU-DOMINIO/api/v1/messages \
  -H "X-API-Key: atz_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "5521999998888",
    "body": "Olá, Maria! Sua consulta é amanhã às 10h.",
    "name": "Maria Souza"
  }'
{ "data": {
  "message_id": 58120, "wa_message_id": "3EB0...", "conversation_id": 1841,
  "line_id": 2, "template": null, "assigned_to": 7
} }

Fora da janela de 24 horas (linhas na API oficial)

Numa linha da API oficial do WhatsApp, a Meta só aceita mensagem iniciada pela empresa se ela usar um modelo aprovado. Nesse caso, em vez de body:

CampoDescrição
template_nameNome do modelo. Exige line_id.
template_languagePadrão pt_BR.
template_paramsAs variáveis, na ordem. Nenhuma pode ser vazia nem conter quebra de linha.

Os modelos disponíveis estão em GET /v1/templates. As recusas são específicas: 404 se o modelo não existe naquela linha, 409 se ainda não foi aprovado pela Meta, 400 se o número de variáveis não bate.

Mensagens enviadas

GET/v1/messages

O que nós enviamos, com o estado de entrega — serve para o seu sistema mostrar se a pessoa recebeu, sem ninguém precisar abrir o atendimento. Não inclui o que o cliente escreveu.

ParâmetroDescrição
line_idLimita a um número.
daysJanela em dias. Padrão 7, máximo 90.
limitPadrão 300, máximo 1000.

A resposta traz total além de data: se total for maior que o número de itens, o limite cortou e há mais para buscar.

{ "data": [
  { "id": 58120, "timestamp": "2026-09-28 11:02:00", "body": "Olá, Maria!...",
    "wa_status": 4, "failed": 0, "wa_message_id": "3EB0...",
    "conversation_id": 1841, "contact_name": "Maria Souza",
    "phone": "5521999998888", "sender_name": "Raphaella" }
], "total": 412, "limit": 300 }

wa_status é um número, e o que interessa é o degrau:

ValorSignifica
0 / 1Ainda saindo.
2Chegou ao servidor do WhatsApp.
3Entregue no aparelho.
4 / 5Lida (5 = áudio ouvido).

Compare por >=, não por igualdade: wa_status >= 3 é "chegou", >= 4 é "leu". failed: 1 marca o que não saiu.

Usuários

GET/v1/users

Quem pode receber uma conversa — para o seu sistema oferecer uma lista em vez de adivinhar ids. Com ?line_id=, quem é do setor daquela linha vem primeiro (no_setor: 0).

{ "data": [
  { "id": 7, "name": "Raphaella", "email": "...", "role": "attendant", "no_setor": 0 }
], "department_id": 3 }

Modelos aprovados

GET/v1/templates

line_id é obrigatório. Mostra o texto exatamente como o cliente recebe, com o estado na Meta e quantas variáveis cada um espera.

{ "data": [
  { "name": "lembrete_consulta", "language": "pt_BR", "status": "APPROVED",
    "body": "Olá {{1}}, sua consulta é {{2}}.", "vars": 2 }
] }

Custo da Meta

GET/v1/pricing

Somente leitura: o que a Meta cobra naquela linha, por categoria (UTILITY, AUTHENTICATION, MARKETING, SERVICE). Exige line_id de uma linha na API oficial, com conta da Meta configurada. ?days= define a janela (sem ele, o mês corrente); ?categorias= filtra.

Webhooks de saída

O caminho inverso: nós avisamos o seu sistema quando algo acontece. Configure em Integrações → Webhooks — URL, segredo e quais eventos. Há um botão de teste que dispara um evento test na hora.

EventoQuando
message.receivedO cliente enviou uma mensagem.
appointment.createdNasceu um agendamento (inclusive os criados pela API).
pix.paidUm pagamento Pix foi confirmado.

Fazemos POST com este corpo:

{
  "event": "message.received",
  "data": {
    "conversation_id": 1841,
    "contact": { "name": "Maria Souza", "phone": "5521999998888" },
    "body": "Bom dia, queria remarcar",
    "message_id": 58121,
    "line_id": 2
  },
  "timestamp": "2026-09-28T14:03:11.204Z"
}

Confirme que veio mesmo de nós

Cada envio leva a assinatura do corpo em HMAC-SHA256, com o segredo que você configurou:

X-Atendize-Event: message.received
X-Atendize-Signature: sha256=3f5a...
User-Agent: Atendize-Webhooks/1.0
// Node.js — valide sobre o corpo CRU, antes de qualquer parse
const esperado = 'sha256=' + require('crypto')
  .createHmac('sha256', SEGREDO).update(corpoCru).digest('hex');
if (esperado !== req.headers['x-atendize-signature']) return res.sendStatus(401);

Responda rápido, com 2xx. Desistimos em 8 segundos e tentamos até 3 vezes, com intervalo crescente. Se o seu endpoint demora, responda primeiro e processe depois — senão uma tarefa lenta vira reentrega e você processa o mesmo evento duas vezes.

Como pode haver reentrega, trate os eventos como repetíveis: use message_id (ou id) para ignorar o que já processou.

Feed de calendário

Antes de escrever qualquer código: se tudo o que você precisa é ver a agenda num calendário, não precisa da API. Há um feed iCal que o Google Agenda, o Outlook e o Apple Calendar assinam direto.

GET/contacts/calendar/feed.ics?token=…

O token é gerado no painel, em Integrações → Feed de calendário. O endereço fica assim:

https://SEU-DOMINIO/contacts/calendar/feed.ics?token=SEU_TOKEN

É só colar em "Assinar por URL" (ou "Adicionar por URL") no aplicativo de calendário. Cada compromisso vira um evento com o serviço e o nome no título, e telefone, profissional e estado na descrição. Entram os agendamentos dos últimos 60 dias em diante, até 1000, já marcados ou concluídos.

Três limites que valem saber antes de escolher este caminho. É somente leitura — nada volta do calendário para cá. A atualização é periódica, não instantânea: o Google costuma reler de poucas em poucas horas, e isso não se controla do nosso lado. E o token vai no endereço, então quem tiver o link vê a agenda — se vazar, gere outro no painel, que o antigo morre na hora.

Para agenda em tempo real ou nos dois sentidos, aí sim é a API de agendamentos.