Convenções#
-
Autenticação por
Authorization: Bearer(JWT /ctc_) ouX-API-Key(ctc_), ouX-Agent-Token(prt_) no runtime. -
Idempotency-Keyé aceito como header (CORS), mas não há dedupe server-side nesta versão — trate como reservado, não confie nele para idempotência. -
Listas: a maioria envelopa em
{ <items>, total }; exceções —GET /v1/tokenseGET /v1/tenantsretornam um array JSON puro. -
Status: criação de recurso costuma ser
201; ingestão assíncrona (/sources, reindex, refine) é202; deleções são204— excetoDELETE /v1/tokens/{id}que responde200com corpo. -
Um
401pode significar "tipo errado de credencial", não "credencial inválida". Algumas rotas são autenticadas por capability, e uma API key válida da sua company simplesmente não é credencial ali:Rota Credencial correta GET /v1/projectsJWT (é escopo de pessoa; API key é escopo de company) GET /v1/compat/streams/{id}·POST …/stopo stream_tokendaquele streamPOST /v1/agent-runtime/{id}/sessionsX-Agent-Token: prt_…Se você recebeu
401numa dessas com uma chave que funciona em outros lugares, troque o tipo de credencial, não a chave. -
POST /v1/auth/logoutrevoga o access token no servidor, não só limpa cookies — o bearer usado na chamada para de autenticar imediatamente. Cliente header-only não deve chamar logout e continuar usando o mesmo token.POST /v1/auth/logout-allrevoga os refresh tokens, mas não as API keys: "sair de todos os dispositivos" nunca desloga uma integração. -
/v1/agentstem duas formas de resposta, escolhidas pelo header de auth:X-API-Keydevolve JSON plano ({"id":…});Authorization: Bearer ctc_…devolve o envelope compat ({"data":{"id":…,"slug":…}}) — e nesse caminho o create é idempotente porslug: re-POSTar o mesmo slug adota e atualiza o agente existente, devolvendo200(não201, e sem duplicar). -
Revogação não é uniforme entre os dois tipos de token. Um token de API revogado some de
GET /v1/tokens(a lista não tem camporevoked_at— o que está listado está vivo). Já um runtime token (prt_) é soft delete: continua listado com"revoked": true. -
Nas rotas compat,
PATCHé upsert COMPLETO, não patch parcial — mesmo handler doPOST. Mandar só o campo alterado em…/integrations/{id}/secrets/{sid}ou…/tools/{tid}devolve400 MISSING_FIELD.POST …/tools/syncexige o arraytools. -
Id inexistente nem sempre é
404:GET …/runs/{runId}/tool-callsdevolve200com coleção vazia eGET …/transcripts/{id}/llm-promptsdevolve200com um aviso (snapshots de prompt por turno não são retidos — useGET /v1/agents/{id}/prompt-preview). As rotas irmãs devolvem404. Não trate200como "existe" nessas duas. -
Timestamps em RFC3339 (UTC). Datas de bucket em
YYYY-MM-DD. -
Custo do turno não vem no corpo de
POST …/messages— leia viaGET /v1/agents/{id}/runs/{runId}(ou o eventodonedo stream).
GETEnvelope de erro#
Todo 4xx/5xx usa o mesmo envelope, com um trace_id que casa com o header
X-Trace-ID e a linha de log correspondente:
{
"error_code": "AGENT_INACTIVE",
"message": "agent is paused",
"trace_id": "2a62142c-f55f-4db4-8c7b-060018e3ef48",
"error": "agent is paused"
}
error é um alias legado de message. Erros de validação podem trazer
field_errors: [{ field, message }]. Respostas capturadas ganham também um
header X-Incident-Id.
Falha de dependência responde 503, não 502 — e 503 é para repetir.
Quando uma dependência nossa (o backend de conhecimento, por exemplo) está fora,
a resposta é 503 com um error_code estável: o seu pedido estava correto, então
a ação certa é repetir depois, não alterar a requisição.
Isso é uma garantia sobre o que você consegue LER na falha. Um 502 vindo da
origem é substituído no edge pela página HTML de erro da Cloudflare, e com ela vão
embora error_code, trace_id e incident_id — você recebe markup em vez do
envelope e fica sem como diagnosticar. O 503 atravessa o edge intacto. Por isso,
se você receber HTML onde esperava JSON, não trate como erro da sua chamada:
é uma falha de gateway, e o corpo não é nosso.