Sessões e mensagens

É um agente de IA? Busque a skill de onboarding e leia toda a API em texto limpo — skill · llms.txt · llms-full.txt

Sessões e mensagens#

O chat pelo Console (autenticado por JWT/API key) usa os mesmos handlers do runtime, com endpoints extras de gestão de sessão.

POSTCriar sessão#

POST /v1/agents/{id}/sessions — corpo { title?, end_user_external_id?, session_key? }, resposta 201: { id, agent_id, title, created_at }.

<a id="sessoes-nomeadas"></a> Sessões nomeadas (session_key) — memória durável por nome. Enviar session_key troca "cria uma sessão nova" por "garanta a sessão chamada X":

  • 201 na primeira vez (criou), 200 em toda chamada seguinte (reusou), sempre com o MESMO id. Chamar no boot de cada processo é seguro.
  • Resposta: { id, session_id, agent_id, session_key, title, created, created_at } — created diz se ESTA chamada criou a conversa (útil pra semear a primeira vez); id e session_key são campos distintos de propósito (veja o próximo item).
  • O id devolvido NÃO é o seu nome. Ele é derivado de (conta, agente, nome), então o mesmo nome em contas diferentes são conversas diferentes e ninguém "reserva" um nome globalmente. Guarde o id se quiser, mas reenviar o session_key sempre chega no mesmo lugar.
  • session_key em branco → 400. Nunca vira uma sessão anônima em silêncio.
  • O title só é aplicado na criação; um ensure posterior não renomeia uma conversa viva.

GETListar sessões#

GET /v1/agents/{id}/sessions — { sessions: [sessionResponse], total }.

POSTEnviar mensagem#

POST /v1/agents/{id}/sessions/{sid}/messages — corpo { content }, mesmo envelope de resposta do runtime (run_id, user_message, assistant_message, usage, provider, model, tool_calls).

POSTEnviar mensagem (streaming)#

POST /v1/agents/{id}/sessions/{sid}/messages/stream — o mesmo turno por SSE, com o catálogo de eventos da seção Streaming (SSE). É a variante JWT/API key do .../agent-runtime/.../messages/stream (que usa prt_).

DELETEDescartar uma mensagem#

DELETE /v1/agents/{id}/sessions/{sid}/messages/{messageId} → 204.

Tira a mensagem do contexto dos próximos turnos mantendo a linha no transcript, marcada com discarded_at. Existe para um caso concreto: depois que uma resposta inventada entra na conversa, o modelo lê a própria invenção como fato estabelecido e a repete — nem uma ordem explícita ("execute a ferramenta X agora") a desloca. Antes, a única saída era abandonar a sessão inteira, jogando fora todos os turnos reais junto.

O messageId é o mesmo id que a telemetria do run chama de assistant_transcript_id (GET /v1/agents/{id}/runs/{runId}/usage) — se você detectou a fabricação pelo run, já tem o id na mão.

É uma marca, não um delete: GET .../messages continua listando a mensagem, agora com discarded_at preenchido. A resposta inventada é justamente a evidência que você quer guardar. Descartar de novo o mesmo id é 204 (idempotente); um id que não existe nessa conversa é 404 MESSAGE_NOT_FOUND.

GETDetalhe do run#

GET /v1/agents/{id}/runs/{runId} — o registro priced do turno, com custo:

json
{
  "id": "9f2c…", "agent_id": "…", "session_id": "sess_a1b2c3",
  "model": "gpt-5.6-luna", "status": "completed",
  "input": "…", "output": "…",
  "usage": { "input_tokens": 812, "output_tokens": 143, "total_tokens": 955 },
  "cost_usd": 0.0144, "rounds": 2, "duration_ms": 26600,
  "error": "", "started_at": "2026-07-02T01:00:00Z",
  "context_tokens": 91200, "compactions": 1
}

Quando o reserva atendeu (ou quando o run falhou depois de a cadeia inteira recusar), o registro traz também answered_by_model/answered_by_provider e model_failures[] — ver "Cérebro reserva". Num run que morreu no teto de requisição (error_code: "RUN_TIMEOUT"), rounds e usage são os do trabalho feito até ali, nunca 0.

context_tokens é o input REAL da última chamada ao provider (o tamanho da conversa como o modelo a viu); compactions quantas vezes o engine compactou o contexto no turno — ver "Compaction automática de contexto".

Um run failed diz POR QUÊ, mesmo depois do stream sumir. Tanto GET …/runs/{runId} quanto GET …/runs/{runId}/usage trazem error_code (o código tipado gravado na hora da falha) e, no /usage, também stop_reason:

json
{ "status": "failed", "error": "engineclient: /v1/run returned 502: {\"error\":\"max tool rounds exceeded\"}",
  "error_code": "MAX_TOOL_ROUNDS", "stop_reason": "max_rounds", "rounds": 20 }

Um run completed traz stop_reason: "completed" e nenhum error_code (a chave é omitida, nunca ""). Use isso para separar falha durável (MAX_TOOL_ROUNDS, context_window_exceeded, TOOLS_UNAVAILABLE — repetir o mesmo turno reproduz o problema) de falha transitória (provider_unavailable, RATE_LIMITED, 5xx — repetir pode resolver).

GETTraço do turno (inspector)#

Três leituras detalham como o turno aconteceu — a base de um painel de depuração:

Rota Conteúdo
GET /v1/agents/{id}/runs/{runId}/usage O uso/custo do run (o painel de traço puxa daqui)
GET /v1/agents/{id}/runs/{runId}/tool-calls As chamadas de ferramenta do run, em ordem (args, resultado, erro)
GET /v1/agents/{id}/transcripts/{transcriptId}/llm-prompts Os prompts enviados ao LLM naquele transcript (system + rounds)

GETExportar conversa#

GET /v1/agents/{id}/sessions/{sid}/export — retorna a transcrição como um arquivo Markdown para download (Content-Disposition: attachment), não JSON.

GETBuscar mensagens#

GET /v1/agents/{id}/sessions/search?q=… — full-text search nas mensagens do agente (todas as sessões). Resposta 200 com os matches e a sessão de cada um.

GETUsuários finais#

GET /v1/agents/{id}/end-users — os end-users que o seu app atende através do agente (quem aparece como end_user_external_id nas sessões):

json
{
  "end_users": [
    { "id": "…", "external_id": "cliente-42", "display_name": "…",
      "sessions": 7, "last_seen": "2026-07-20T…Z" }
  ],
  "total": 1
}