Agentes#
O grupo /v1/agents cobre o ciclo de vida do agente. As leituras aceitam
JWT/API key; criar/editar/deletar exigem papel owner ou admin (o /v1/agents
CRUD é servido pela superfície que atende Console e CRM). O shape canônico do
agente (agentResponse) é retornado por get, create, clone, update e restore.
GETDetalhe do agente#
GET /v1/agents/{id} — resposta 200:
{
"id": "…", "company_id": 30, "tenant_id": 0,
"name": "Atendimento", "description": "…", "system_prompt": "…",
"model": "gpt-5.4-mini", "output_format": "markdown",
"tools": ["brain_search", "web_search"], "skills": ["tdd"],
"active": true,
"rag_auto": true, "rag_top_k": 5, "rag_qa": true,
"conversation_recall": false,
"legacy_mode": false, "brain_mode": true,
"max_tokens": 0, "temperature": null, "reasoning_effort": "",
"max_rounds_per_run": 20, "run_class": "", "monthly_budget_usd": 0,
"created_at": "2026-06-16T…Z", "updated_at": "2026-07-01T…Z"
}
temperature é anulável (null = default do provedor). brain_mode é derivado
(!legacy_mode) e é alias deprecado — use legacy_mode.
GETListar agentes#
GET /v1/agents — aceita ?tenant_id= (id interno) ou ?tenant_external_id=
(o seu identificador do tenant, para não precisar traduzir via /v1/tenants);
resposta 200 { agents: [agentResponse], total }. Um tenant_external_id
desconhecido devolve 404 TENANT_NOT_FOUND — de propósito, e não uma lista vazia:
"esse tenant não existe" e "esse tenant não tem agentes" são respostas diferentes,
e só a primeira significa que o seu mapeamento está errado.
POSTCriar agente#
POST /v1/agents (owner/admin) — corpo agentRequest:
| Campo | Tipo | Default / regra |
|---|---|---|
name |
string | obrigatório |
tenant_id |
uint | 0 = tenant padrão do projeto |
tenant_external_id |
string | resolve-ou-cria o tenant pelo id externo (clínica = tenant) |
description |
string | — |
system_prompt |
string | — |
model |
string | default gpt-5.4-mini |
output_format |
string | default markdown |
tools |
string[] | — |
skills |
string[] | — |
rag_auto |
bool? | null → true |
rag_top_k |
int? | null → 5; clamp 1..20 |
rag_qa |
bool? | null → true (qa_first) |
conversation_recall |
bool? | null → false. Quando true, o agente recupera semanticamente trechos das conversas ANTERIORES do MESMO end-user (via brain_search), escopado por end_user_external_id — sessões abertas sem end-user não recuperam nada. |
max_tokens |
int? | null/0 → default do provedor; clamp 0..64000 |
temperature |
float? | null → default; clamp 0..2 |
reasoning_effort |
string? | low|medium|high|xhigh|max; valor fora do conjunto → 400 INVALID_REASONING_EFFORT (nunca cai no default em silêncio) |
max_rounds_per_run |
int? | null/0 → 20; clamp 1..50 (1..200 se run_class="coder") |
run_class |
string? | null/""/desconhecido → padrão (teto 50 rounds / 600s de wall clock). "coder" → teto 200 rounds / 1800s. Ver Classes de run abaixo. |
monthly_budget_usd |
float? | null/≤0 → sem teto por agente |
legacy_mode |
bool? | true = caminho legado deprecado (brain é o default) |
Resposta 201: agentResponse. Erros: AGENT_NAME_REQUIRED, TENANT_NOT_FOUND.
PATCHAtualizar agente#
PATCH /v1/agents/{id} (owner/admin) — update esparso: só os campos presentes no
corpo são aplicados. temperature: null e reasoning_effort: null limpam
explicitamente para o default do provedor. Campos-array são replace total do
campo, nunca merge: {"tools": ["nova"]} substitui o catálogo inteiro por
["nova"] — para ADICIONAR uma entrada, envie a união do que já estava + a nova
(leia antes com GET /v1/agents/{id}). Resposta 200: agentResponse.
POSTClonar agente#
POST /v1/agents/{id}/clone (owner/admin) — sem corpo; cria um agente novo
"<nome> (cópia)" com a mesma config. Resposta 201: agentResponse.
DELETEExcluir agente#
DELETE /v1/agents/{id} (owner/admin) — resposta 204.
POSTAvaliar (eval)#
POST /v1/agents/{id}/eval — roda o prompt montado do agente para um input
contra 1..N modelos, de forma efêmera (nada persiste: sem mensagem, sem
run). É a base do replay (1 modelo) e da comparação A/B (N modelos, executados em
paralelo, teto interno por chamada): resposta, uso, custo e latência lado a lado.
curl -X POST https://agents-api.catcher.one/v1/agents/AGENT_ID/eval \
-H "X-API-Key: ctc_SUA_KEY" -H "Content-Type: application/json" \
-d '{ "input": "Quero cancelar meu pedido", "models": ["gpt-5.4-mini", "gpt-5.4"] }'
Resposta 200 { results: [ { model, content, thinking?, usage, cost_usd, duration_ms, error? } ] }.
GETVersões#
GET /v1/agents/{id}/versions — histórico de snapshots da config do agente:
200 { versions: [ { id, created_at, snapshot } ], total } (o snapshot é a
config completa decodificada).
POSTRestaurar versão#
POST /v1/agents/{id}/versions/{vid}/restore — sem corpo; volta a config do
agente ao snapshot vid. Resposta 200: agentResponse. Skills e ferramentas
têm o mesmo par de rotas (/v1/skills/{id}/versions…, /v1/tools/{id}/versions…).
GETEstatísticas#
GET /v1/agents/stats — { stats: { "<agentId>": { sessions, runs } } }.
GETClasses de run (run_class)#
Um agente executa sob um perfil de limites, escolhido pelo campo run_class:
run_class |
Teto de max_rounds_per_run |
Wall clock por turno | Janela do stream_token |
|---|---|---|---|
"" (default) |
50 |
600s (10 min) |
30 min |
"coder" |
200 |
1800s (30 min) |
60 min |
Escrever software é a carga mais tool-heavy e mais longa que a plataforma
atende — uma run real encadeia dezenas de ciclos ler → editar → compilar → ler
erro → corrigir, e pode passar de dez minutos. A classe coder existe para esse
caso e é opt-in por agente, então nada muda para os demais.
Três regras que evitam surpresa:
- A classe move o TETO, não o DEFAULT. Um agente
codersemmax_rounds_per_runcontinua rodando com os20rounds padrão — profundidade ainda se pede explicitamente. - Valor desconhecido cai no padrão (fail closed).
"CODER "é normalizado (trim + caixa);"turbo"vira"". Um agente criado antes do campo existir mantém exatamente os limites de hoje. - O override por dispatch é clampado contra a classe do AGENTE. Mandar
max_rounds_per_run: 120no corpo do dispatch de um agente padrão resulta em50— um dispatch reajusta profundidade dentro do que o agente recebeu, nunca compra um teto maior.
O corpo do dispatch também aceita, por run (sem reprovisionar o agente):
model (qualquer id que o runtime roteia — ex.: k3, MiniMax-M3; ausente ou
vazio = o modelo do agente) e reasoning_effort
(low|medium|high|xhigh|max; valor fora do conjunto → 400 INVALID_REASONING_EFFORT, nunca fallback silencioso).
GET /v1/projects/{projectId}/agents/{agentId}/limits publica a tabela acima em
run_classes, para você ler os tetos em vez de descobri-los como um corte no meio
da run.