API de integração do Atendai
Envie mensagens de WhatsApp, registre notas e etiquetas e leia conversas do seu escritório a partir do seu sistema ou da sua IA.
Chave e escopos
Toda chamada leva a chave no cabeçalho Authorization: Bearer. A chave é emitida pelo Dono do escritório, em Configurações, aba Chaves. Ela aparece uma única vez; guarde em lugar seguro.
Cada chave tem escopos, e pode ficar restrita a alguns números do escritório.
| Escopo | Permite |
|---|---|
mensagem:enviar | Enviar texto, template e imagem, e consultar envios |
nota:criar | Criar nota interna |
etiqueta:aplicar | Aplicar etiquetas |
contato:gravar | Exigido quando o corpo traz telefone_e164 |
conversa:ler | Ler uma conversa |
mensagem:ler | Ler as mensagens de uma conversa, com o texto |
# <marca>_api_<8 letras ou números>_<43 caracteres>
Authorization: Bearer atendai_api_ab12cd34_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Regras comuns
Para quem enviar
Todo POST indica o alvo de um destes dois jeitos: conversa_id, ou canal_id junto com telefone_e164. O canal_id vem da rota Situação.
Idempotência
Todo POST exige uma chave de idempotência, no cabeçalho Idempotency-Key ou no campo chave_idempotencia, com 8 a 120 caracteres. Repetir a mesma chave com o mesmo corpo devolve o mesmo resultado e não envia duas vezes. A mesma chave com outro corpo dá 409.
Corpo
Até 64 KiB. Campo que a rota não conhece é recusado com 422.
Mensagens privadas e grupos
Conversas privadas e de grupo nunca são devolvidas pela leitura: respondem 404, como se não existissem.
Authorization: Bearer $CHAVE Content-Type: application/json Idempotency-Key: pedido-2026-10-12-0001
Situação
/api/v1/healthConfirma que a chave funciona e lista os números que ela alcança, com o estado de cada um.
Resposta
| Campo | Descrição |
|---|---|
status | ok ou degradado |
canais[].id | Use como canal_id nos envios |
canais[].estado | conectado, em_revisao ou desconectado |
curl -sS "https://app.atendai.pro/api/v1/health" \ -H "Authorization: Bearer $CHAVE"
const r = await fetch("https://app.atendai.pro/api/v1/health", {
headers: { Authorization: `Bearer ${process.env.CHAVE}` },
});
console.log(await r.json());
import os, requests
r = requests.get("https://app.atendai.pro/api/v1/health",
headers={"Authorization": f"Bearer {os.environ['CHAVE']}"})
print(r.json())
{
"status": "ok",
"versao": "1",
"escritorio_id": "8f1c…",
"canais": [
{ "id": "6e68…", "nome": "Comercial", "estado": "conectado" }
]
}
Enviar texto
/api/v1/whatsapp/textomensagem:enviarEnvia uma mensagem de texto. No WhatsApp oficial, só funciona com a janela de 24 horas aberta; fora dela, use um template.
Corpo
| Campo | Tipo | Descrição |
|---|---|---|
conversa_id | uuid | Ou canal_id + telefone_e164 |
texto obrigatório | string | 1 a 4096 caracteres |
nota | string | Nota interna junto, até 4000 (exige nota:criar) |
etiquetas | string[] | 1 a 10 nomes (exige etiqueta:aplicar) |
Resposta
200 quando o WhatsApp confirma em até 6 segundos. 202 com estado: "pendente" quando ainda não confirmou: consulte depois pela chave de idempotência.
curl -sS -X POST "https://app.atendai.pro/api/v1/whatsapp/texto" \
-H "Authorization: Bearer $CHAVE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: texto-0001-2026-10-12" \
-d '{
"conversa_id": "0b6d1f2e-…",
"texto": "Olá, seu pedido teve andamento."
}'
const r = await fetch("https://app.atendai.pro/api/v1/whatsapp/texto", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CHAVE}`,
"Content-Type": "application/json",
"Idempotency-Key": "texto-0001-2026-10-12",
},
body: JSON.stringify({
conversa_id: "0b6d1f2e-…",
texto: "Olá, seu pedido teve andamento.",
}),
});
console.log(r.status, await r.json());
import os, requests
r = requests.post(
"https://app.atendai.pro/api/v1/whatsapp/texto",
headers={"Authorization": f"Bearer {os.environ['CHAVE']}",
"Idempotency-Key": "texto-0001-2026-10-12"},
json={"conversa_id": "0b6d1f2e-…",
"texto": "Olá, seu pedido teve andamento."})
print(r.status_code, r.json())
{
"mensagem_id": "a41c…",
"conversa_id": "0b6d…",
"estado": "enviada",
"wamid": "wamid.HBgM…",
"responsavel": { "tipo": "ia" }
}
Enviar template
/api/v1/whatsapp/templatemensagem:enviarEnvia um template aprovado pela Meta. Serve para iniciar conversa ou falar fora da janela de 24 horas.
Corpo
| Campo | Tipo | Descrição |
|---|---|---|
template obrigatório | string | Nome do template, letras minúsculas, números e _ |
idioma obrigatório | string | Ex.: pt_BR |
corpo | string[] | Variáveis do corpo, em ordem (até 60) |
cabecalho | string[] | Variável do cabeçalho (no máximo 1) |
botao_url | string | Parte variável do botão de link |
nome | string | Nome do contato, se ele ainda não existir (até 120) |
nota, etiquetas | Como no envio de texto |
curl -sS -X POST "https://app.atendai.pro/api/v1/whatsapp/template" \
-H "Authorization: Bearer $CHAVE" \
-H "Content-Type: application/json" \
-d '{
"canal_id": "6e6834d2-…",
"telefone_e164": "+5511999998888",
"nome": "Maria Silva",
"template": "aviso_audiencia",
"idioma": "pt_BR",
"corpo": ["Maria", "15/10 às 14h"],
"chave_idempotencia": "tpl-0001-2026-10-12"
}'
await fetch("https://app.atendai.pro/api/v1/whatsapp/template", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.CHAVE}`, "Content-Type": "application/json" },
body: JSON.stringify({
canal_id: "6e6834d2-…",
telefone_e164: "+5511999998888",
nome: "Maria Silva",
template: "aviso_audiencia",
idioma: "pt_BR",
corpo: ["Maria", "15/10 às 14h"],
chave_idempotencia: "tpl-0001-2026-10-12",
}),
});
requests.post(
"https://app.atendai.pro/api/v1/whatsapp/template",
headers={"Authorization": f"Bearer {os.environ['CHAVE']}"},
json={"canal_id": "6e6834d2-…", "telefone_e164": "+5511999998888",
"nome": "Maria Silva", "template": "aviso_audiencia",
"idioma": "pt_BR", "corpo": ["Maria", "15/10 às 14h"],
"chave_idempotencia": "tpl-0001-2026-10-12"})
Enviar imagem
/api/v1/whatsapp/imagemmensagem:enviarEnvia uma imagem a partir de um endereço público. Aceita só https://. Não aceita nota nem etiquetas no mesmo pedido.
| Campo | Tipo | Descrição |
|---|---|---|
imagem_url obrigatório | string | Endereço https, até 2048 |
legenda | string | Até 1024 caracteres |
curl -sS -X POST "https://app.atendai.pro/api/v1/whatsapp/imagem" \
-H "Authorization: Bearer $CHAVE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: img-0001-2026-10-12" \
-d '{
"conversa_id": "0b6d1f2e-…",
"imagem_url": "https://exemplo.com.br/comprovante.jpg",
"legenda": "Segue o comprovante."
}'
Consultar envio
/api/v1/whatsapp/template?chave=…mensagem:enviarDevolve o resultado de qualquer envio pela chave de idempotência usada nele: o mesmo corpo e o mesmo status do POST, mais o pedido_id. Use quando o envio respondeu 202 ou quando a conexão caiu sem resposta.
CHAVE_ENCERRADA. Só consulte chaves que você já enviou.curl -sS "https://app.atendai.pro/api/v1/whatsapp/template?chave=texto-0001-2026-10-12" \ -H "Authorization: Bearer $CHAVE"
Ler conversa
/api/v1/conversas/{id}conversa:lerDevolve os dados da conversa, sem o texto das mensagens (para o texto, use Mensagens da conversa). Não aceita parâmetros na URL.
Conversa privada, de grupo, de outro escritório ou de um número que a chave não alcança responde 404.
curl -sS "https://app.atendai.pro/api/v1/conversas/0b6d1f2e-…" \ -H "Authorization: Bearer $CHAVE"
{
"id": "0b6d…",
"estado": "aguardando",
"canal_id": "6e68…",
"contato": { "id": "c912…", "nome": "Maria Silva", "telefone": "+5511999998888" },
"responsavel": { "tipo": "fila", "id": "7a10…" },
"etiquetas": ["Urgente"],
"criada_em": "2026-10-10T12:00:00Z",
"ultima_entrada_em": "2026-10-11T09:00:00Z",
"atualizada_em": "2026-10-11T09:01:00Z"
}
Mensagens da conversa
/api/v1/conversas/{id}/mensagensmensagem:lerDevolve as mensagens de uma conversa, da mais nova para a mais antiga. É a única rota que devolve o texto das mensagens, por isso exige um escopo próprio, que o Dono marca ao gerar a chave.
Parâmetros na https://app.atendai.pro/api/v1/conversas/0b6d1f2e-…/mensagens?limite=50
| Parâmetro | Tipo | Descrição |
|---|---|---|
limite | inteiro | Quantas mensagens por página, de 1 a 100. Padrão 50. Valor abaixo de 1 vira 1 e acima de 100 vira 100 |
antes | uuid | Cursor: o id de uma mensagem desta conversa. Traz só as mensagens mais antigas que ela. Use o proximo_antes da página anterior |
Outro parâmetro, parâmetro repetido, limite que não é número ou antes que não é uuid respondem 422 VALIDACAO.
Resposta
| Campo | Descrição |
|---|---|
conversa_id | A conversa pedida |
mensagens[].id | Id da mensagem (serve de cursor em antes) |
mensagens[].direcao | entrada (veio do cliente) ou saida |
mensagens[].tipo | texto, imagem, audio, video, documento, figurinha, localizacao, contato, template, lista ou botoes |
mensagens[].texto | O texto da mensagem, ou null |
mensagens[].em | Data e hora da mensagem |
mensagens[].status | pendente, enviada, entregue, lida ou falhou |
mensagens[].origem | Quem gerou: contato, atendente, ia, fluxo, automacao, agendada, aparelho, equipe ou integracao |
mensagens[].erro_meta | Só quando status é falhou: { "codigo": 131047 }, o código numérico do WhatsApp. Nos outros casos, null |
proximo_antes | O cursor da próxima página, ou null quando acabou |
Regras
Mensagem apagada, pela equipe ou pelo cliente, vem na lista sem o texto (texto: null). Mensagem editada vem só com o texto atual; a versão anterior não é devolvida.
Registros internos da equipe, como transferência e encerramento, não aparecem. A resposta não traz arquivo de mídia, transcrição nem autor.
Conversa privada, de grupo, de outro escritório ou de um número que a chave não alcança responde 404, assim como um antes que não é uma mensagem desta conversa. Chave sem o escopo mensagem:ler responde 403 ESCOPO_AUSENTE.
curl -sS "https://app.atendai.pro/api/v1/conversas/0b6d1f2e-…/mensagens?limite=50" \
-H "Authorization: Bearer $CHAVE"
# próxima página: o proximo_antes da resposta anterior
curl -sS "https://app.atendai.pro/api/v1/conversas/0b6d1f2e-…/mensagens?limite=50&antes=9c41b7e0-…" \
-H "Authorization: Bearer $CHAVE"
const base = "https://app.atendai.pro/api/v1/conversas/0b6d1f2e-…/mensagens";
let antes = null;
do {
const url = base + "?limite=100" + (antes ? `&antes=${antes}` : "");
const r = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.CHAVE}` },
});
const pagina = await r.json();
for (const m of pagina.mensagens) console.log(m.em, m.direcao, m.texto);
antes = pagina.proximo_antes;
} while (antes);
import os, requests
base = "https://app.atendai.pro/api/v1/conversas/0b6d1f2e-…/mensagens"
antes = None
while True:
params = {"limite": 100}
if antes:
params["antes"] = antes
r = requests.get(base, params=params,
headers={"Authorization": f"Bearer {os.environ['CHAVE']}"})
pagina = r.json()
for m in pagina["mensagens"]:
print(m["em"], m["direcao"], m["texto"])
antes = pagina["proximo_antes"]
if not antes:
break
{
"conversa_id": "0b6d…",
"mensagens": [
{
"id": "5d2a…",
"direcao": "saida",
"tipo": "texto",
"texto": "Olá, seu pedido teve andamento.",
"em": "2026-10-11T09:01:00Z",
"status": "lida",
"origem": "integracao",
"erro_meta": null
},
{
"id": "9c41…",
"direcao": "entrada",
"tipo": "texto",
"texto": "Bom dia, alguma novidade?",
"em": "2026-10-11T09:00:00Z",
"status": "entregue",
"origem": "contato",
"erro_meta": null
}
],
"proximo_antes": "9c41…"
}
Nota interna
/api/v1/conversas/notanota:criarRegistra uma nota que só a equipe vê. Nada é enviado ao cliente. Funciona também em conversas privadas.
| Campo | Tipo | Descrição |
|---|---|---|
nota obrigatório | string | 1 a 4000 caracteres |
nome, etiquetas | Opcionais |
curl -sS -X POST "https://app.atendai.pro/api/v1/conversas/nota" \
-H "Authorization: Bearer $CHAVE" \
-H "Content-Type: application/json" \
-d '{
"conversa_id": "0b6d1f2e-…",
"nota": "Cliente pediu retorno amanhã.",
"chave_idempotencia": "nota-0001-2026-10-12"
}'
{ "conversa_id": "0b6d…", "contato_id": "c912…", "nota_id": "e7f0…" }
Etiquetas
/api/v1/conversas/etiquetasetiqueta:aplicarAplica etiquetas que já existem no escritório, pelo nome. Etiqueta que não existe, ou com nome repetido, é recusada com 422: a API não cria etiqueta.
curl -sS -X POST "https://app.atendai.pro/api/v1/conversas/etiquetas" \
-H "Authorization: Bearer $CHAVE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: etq-0001-2026-10-12" \
-d '{ "conversa_id": "0b6d1f2e-…", "etiquetas": ["Urgente"] }'
Erros
Todo erro vem no mesmo formato, com um código fixo para o seu sistema tratar e uma frase para pessoas.
| HTTP | Código | Quando |
|---|---|---|
| 400 | CHAVE_IDEMPOTENCIA_AUSENTE | POST sem chave de idempotência |
| 401 | corpo vazio | Chave ausente, errada ou revogada, ou módulo desligado |
| 403 | ESCOPO_AUSENTE, CANAL_NAO_PERMITIDO | A chave não tem o escopo ou o número |
| 404 | NAO_ENCONTRADO | Rota, conversa, número ou cursor inexistente |
| 409 | IDEMPOTENCIA_CONFLITO | Mesma chave com outro corpo |
| 409 | CHAVE_ENCERRADA | Chave encerrada por uma consulta antes do envio |
| 413 | CORPO_GRANDE | Corpo acima de 64 KiB |
| 422 | VALIDACAO | Campo inválido ou a mais; vem com erros_campos |
| 422 | JANELA_FECHADA | Texto fora da janela de 24 horas |
| 422 | TEMPLATE_INDISPONIVEL | Template inexistente ou não aprovado |
| 422 | ETIQUETA_NAO_ENCONTRADA | Etiqueta que não existe no escritório |
| 422 | ERRO_META | O WhatsApp recusou; vem com o código da Meta |
| 429 | LIMITE | Passou do limite; respeite o retry-after |
| 503 | INDISPONIVEL | Falhou antes de registrar; pode repetir com a mesma chave |
{
"erro": "VALIDACAO",
"mensagem": "Algum campo está inválido.",
"erros_campos": [
{ "campo": "texto", "motivo": "valor inválido" }
]
}
Em construção
| Rota | O que faz |
|---|---|
GET /api/v1/contatos/{id} | Dados de um contato, por id ou telefone |
GET /api/v1/conversas | Lista de conversas, filtrando por data, estado e número |
| Webhooks | Aviso no seu sistema a cada mensagem recebida, com assinatura para conferir a origem. O texto da mensagem é opcional e só o Dono liga |