A caixa de entrada do Chat — conversas, mensagens, contatos e equipes — é servida por uma API
v1 em /api/v1/chat/. Ela é anterior às convenções da v2 e não as compartilha. Leia
esta página uma vez antes de escrever código contra ela; todo o resto da Referência da API
descreve a v2.
URL base e autenticação
curl -X GET "https://agent-studio.seeyu.ai/api/v1/chat/conversations?workspaceId=WORKSPACE_ID" \
-H "X-API-Key: YOUR_API_KEY"A autenticação usa o mesmo header X-API-Key do resto da API — veja
Autenticação.
Toda operação nomeia um workspace
workspaceId é um parâmetro de query obrigatório em toda leitura e um campo de body
obrigatório em toda escrita. Não existe workspace implícito: uma requisição sem ele é 400.
Uma chave de API com escopo de workspace nunca alcança nada além do próprio workspace. Se a
requisição nomear um workspaceId diferente, a API responde 403 antes de qualquer consulta.
Veja Escopo de workspace para as regras
completas e as três mensagens 403 distintas.
Como os recursos são sempre resolvidos dentro de workspaceId, uma conversa, um contato ou
uma equipe que vive em outro workspace é reportado como 404 (não encontrado), e não como
acesso negado — a API nunca é um oráculo de existência para um workspace que você não alcança.
As respostas são objetos simples, não { data }
A v2 envolve todo sucesso em { "data": ... }. A v1 não:
{ "conversation": { "id": "conv_...", "status": "open" } }{ "conversations": [ ... ], "pagination": { "page": 1, "perPage": 25, "total": 42, "totalPages": 2 } }Os erros são planos
A v2 responde { "error": { "code": "...", "message": "..." } }. A v1 responde um objeto
plano cujo único campo garantido é error:
| Status | Corpo | Significado |
|---|---|---|
400 | { error, details } | A requisição falhou na validação. details lista os campos com problema. |
401 | { error } | O header X-API-Key está ausente, malformado, expirado ou revogado. |
403 | { error } | A chave não pode alcançar este workspace. |
404 | { error } | Não existe esse recurso neste workspace. |
409 | { error, code, ... } | A requisição colide com o estado existente. Veja abaixo. |
429 | { error, message, retryAfter } | Limite de requisições excedido. Prefira o header Retry-After, que está em segundos. |
500 | { error, requestId } | Erro inesperado no servidor. Cite o requestId ao abrir um pedido de suporte. |
Ramifique pelo status HTTP e, no caso de um 409, pelo code. A string error é
legível por humanos e não é um contrato estável.
Como se recuperar de um 409
Duas operações podem entrar em conflito, e as duas devolvem o registro que está bloqueando, para que você o reutilize em vez de tentar de novo.
POST /api/v1/chat/conversations → code: "active_conversation_exists". O contato já
tem uma conversa em andamento naquela caixa de entrada. O campo conversation traz essa
conversa — continue a thread por lá. Conversas nas outras caixas de entrada do contato não
bloqueiam.
POST /api/v1/chat/contacts → code: "PHONE_ALREADY_EXISTS". Outro contato do workspace
já tem esse número de telefone, normalizado para apenas dígitos. A resposta traz
contactId, contactName, phone e conversationId — a conversa mais recentemente ativa do
contato existente, ou null.
Paginação
A maioria das listas do chat v1 pagina por offset, com page e perPage (ambos inteiros;
perPage tem teto de 100), e retorna um objeto pagination:
{ "page": 2, "perPage": 25, "total": 130, "totalPages": 6 }A paginação por offset não é um snapshot estável. As conversas são ordenadas pela atividade
mais recente, então uma conversa cuja atividade muda entre duas requisições pode aparecer em
duas páginas ou em nenhuma. Para uma varredura consistente, filtre por um status fixo ou
processe as páginas de trás para frente.
Dois endpoints se comportam de forma diferente, ambos preservados do lançamento original:
GET /api/v1/chat/contactsusalimitem vez deperPage, e o objetopaginationtrazlimitem vez deperPage.GET /api/v1/chat/conversations/{conversationId}/messagesé paginado por cursor. Omitabeforepara pegar a página mais recente e depois envie ometa.beforede cada resposta para caminhar para trás atémeta.hasMoreserfalse. Dentro de uma página, as mensagens vêm da mais antiga para a mais nova.
Mensagens interativas no WhatsApp
Numa caixa de entrada do WhatsApp, uma mensagem pode dar ao contato opções para tocar em vez
de uma resposta para digitar. Envie as opções em contentAttributes.items no
Enviar Mensagem, ou na primeira mensagem do
Criar Conversa:
curl -X POST "https://agent-studio.seeyu.ai/api/v1/chat/conversations/CONVERSATION_ID/messages" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workspaceId": "WORKSPACE_ID",
"content": "Selecione o setor com o qual deseja falar:",
"contentAttributes": {
"button": "Menu",
"sectionTitle": "Setores",
"items": [
{ "title": "Financeiro", "value": "1" },
{ "title": "Comercial", "value": "2" },
{ "title": "Secretaria", "value": "3" },
{ "title": "Livraria", "value": "4" }
]
}
}'content é o texto mostrado acima das opções. Cada item é { title, value, description? }:
o contato vê o title, e o value volta quando ele toca na opção, então dê a cada item um
value diferente. A quantidade de itens define como eles aparecem:
| Itens | Enviados como | title | description | value |
|---|---|---|---|---|
| 1 a 3 | Botões de resposta | até 20 caracteres | não aparece | até 256 caracteres |
| 4 a 10 | Uma lista que o contato abre por um botão | até 24 caracteres | até 72 caracteres | até 200 caracteres |
Uma lista envia os 10 primeiros itens e descarta o resto. Outras duas chaves definem os textos dela, e os botões de resposta ignoram as duas:
| Chave | O que define | Limite | Padrão |
|---|---|---|---|
button | O texto do botão que abre a lista | 20 caracteres | Selecionar |
sectionTitle | O título acima das opções | 24 caracteres | Opções |
Um title, uma description ou um texto acima do limite é cortado para caber em vez de ser
recusado, e um texto em branco volta ao padrão. O value é enviado como está, então mantenha-o
dentro do limite. Deixe o contentType no padrão: o WhatsApp lê items qualquer que seja o
tipo.
O toque do contato chega como uma mensagem recebida cujo content é o title da opção. O
contentAttributes.interactive dela traz a resposta do WhatsApp, com o seu value em
button_reply.id ou list_reply.id. Compare pelo value, não pelo título.
Opções são uma mensagem livre, então o WhatsApp só as entrega dentro da janela de 24 horas que se abre cada vez que o contato escreve para você. Fora dela, comece com um template aprovado.
Limites de requisições
Toda resposta traz X-RateLimit-Limit, X-RateLimit-Remaining e
X-RateLimit-Reset. Um 429 acrescenta Retry-After em segundos — adicione jitter em vez de
tentar de novo exatamente naquele instante. Os limites são por plano e compartilhados com o
resto da API.