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. -
429é sempre o envelope JSON, comerror_code: "RATE_LIMITED"— nunca umtext/plain "Too Many Requests". Os headers RFC 6585 vêm juntos:Retry-After,X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset. O limite é por conta (todas as suas chaves, tokens de runtime e o Console compartilham o mesmo orçamento) e o valor vigente está emGET …/limits→requests_per_minute. Trate como transitório: espereRetry-Aftere repita; não é sinal de indisponibilidade.json{ "error_code": "RATE_LIMITED", "message": "rate limit exceeded: 60 requests per minute; retry after 60s", "trace_id": "…", "error": "rate limit exceeded: 60 requests per minute; retry after 60s" } -
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 — retry automático exige contrato.
Quando uma dependência nossa (o backend de conhecimento, por exemplo) está fora,
a resposta é 503 com um error_code estável: o pedido pode estar correto, mas o
estado é indeterminado. Repita automaticamente apenas uma operação idempotente
quando a resposta trouxer Retry-After; honre o valor, use jitter e limite as
tentativas. Sem esse header, não presuma que a falha é transitória.
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.
GETTrace distribuído (traceparent)#
A API participa de traces distribuídos W3C do Catcher Debug. Se você envia
traceparent: 00-<trace_id>-<span_id>-01, ele só é continuado quando o
caller é um peer da malha (prova com X-Ctc-Trace-Token, um segredo
combinado por deployment) — nesse caso a resposta ecoa X-Ctc-Trace-Id e os
spans do brain (uma entrada por rodada do modelo e por chamada de ferramenta,
sem prompt/saída) aparecem no seu workspace do Debug sob o mesmo
trace_id, inclusive os do dispatch assíncrono. Sem o token, o header é
ignorado silenciosamente e nada muda na resposta. Quer o seu produto na
malha? Peça o pareamento (um ctc_dbg_ do seu workspace + o token).
O que os spans carregam (para o seu Trace Explorer). Cada span llm traz
em metadata: round, model, provider, finish_reason, input_tokens,
output_tokens, cache_read_tokens (0 sem cache hit), total_tokens e
cost_usd (a mesma regra de preço do cost_usd do run — provider de
assinatura = 0); o status_code do span é o HTTP do provider (200 numa
rodada completa, 429/5xx numa recusa, 0 sem resposta). Cada span tool
traz tool_name, url_host e, quando falha, error (resumo redigido ≤1 KiB —
{"error_code","message"} se o corpo era o envelope) + exception_class
(ToolHTTP503, ToolTimeout, ToolNetwork); o status_code é o HTTP que a
tool respondeu. Toda falha também vira um evento error no mesmo trace_id
com span_id do span falho, exception_class (Provider429, ProviderTimeout,
ToolHTTP503…), exception_message e stack_trace. O span http raiz do
engine (metadata.component = "engine") agrega rounds, tool_calls,
total_tokens e cost_usd do run. Nunca viaja prompt, resposta, thinking,
argumentos ou saída de ferramenta.