Chat API (v1)

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:

StatusCorpoSignificado
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/contacts usa limit em vez de perPage, e o objeto pagination traz limit em vez de perPage.
  • GET /api/v1/chat/conversations/{conversationId}/messages é paginado por cursor. Omita before para pegar a página mais recente e depois envie o meta.before de cada resposta para caminhar para trás até meta.hasMore ser false. 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:

ItensEnviados comotitledescriptionvalue
1 a 3Botões de respostaaté 20 caracteresnão apareceaté 256 caracteres
4 a 10Uma lista que o contato abre por um botãoaté 24 caracteresaté 72 caracteresaté 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:

ChaveO que defineLimitePadrão
buttonO texto do botão que abre a lista20 caracteresSelecionar
sectionTitleO título acima das opções24 caracteresOpçõ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.