Agents
Crie e versiona configurações de agents dentro de um projeto.
#Endpoints
| Método | Caminho | Descrição |
|---|---|---|
| POST | /v1/projects/:projectId/agents | Cria um agent e publica a versão 1. |
| POST | /v1/projects/:projectId/agents/:id | Aplica patch com lock otimista e publica nova versão. |
| POST | /v1/projects/:projectId/agents/:id/duplicate | Duplica o agent como um agent novo (v1), com overrides opcionais de nome e modelo. |
| POST | /v1/projects/:projectId/agents/:id/archive | Arquiva o agent. |
| POST | /v1/projects/:projectId/agents/:id/versions/:version/restore | Restaura uma versão histórica como nova versão corrente. |
| PUT | /v1/projects/:projectId/agents/:id/mcp-servers/:name/headers | Rotaciona os headers de um servidor MCP sem publicar versão. |
| GET | /v1/projects/:projectId/agents/:id/versions | Lista versões por cursor. |
| GET | /v1/projects/:projectId/agents/:id | Lê a versão atual ou uma versão específica. |
| GET | /v1/projects/:projectId/agents | Lista agents ATIVOS por cursor, com filtros opcionais de nome e arquivamento. |
#Criar agent
const agent = await client.agents.create({
name: 'Assistente operacional',
model: 'anthropic/claude-haiku-4-5',
system: 'Responda com clareza e peça dados faltantes antes de concluir.',
tools: ['web_search', 'memory_read'],
});#Atualizar agent
Atualizações exigem a version atual. Se outra mudança publicou uma versão mais nova, a API retorna conflito (agent.version_conflict, 409).
const updated = await client.agents.update(agent.id, {
version: agent.version,
system: 'Responda com clareza, peça dados faltantes e cite fontes quando pesquisar.',
});O corpo tem semântica de patch: campo omitido preserva o valor atual. Campos de lista (tools, skills, serverlessTools, knowledgeBases) e objetos de configuração (defaults, multiagent, browser) substituem o valor inteiro — não há merge campo a campo. A exceção é metadata, que mescla por chave.
Um patch que não muda nada é no-op: o agent volta na resposta sem publicar versão nova.
#Limpar campos no update
Alguns campos não têm "valor vazio" representável: o schema exige objeto preenchido, então mandar o objeto vazio é inválido e omitir o campo significa preservar. Para esses, a limpeza tem sinal próprio — null.
| Sinal | Efeito |
|---|---|
multiagent: null | Desliga o coordenador (o agent volta a ser um agent comum). |
browser: null | Volta ao browser self-hosted (o padrão). |
defaults: null | Limpa os padrões de execução (ambiente, cofres e memória de uma vez). |
recordBrowser: false ou null | Desliga a gravação de browser para replay. |
Onde o valor vazio existe, ele é o sinal — sem null:
| Sinal | Efeito |
|---|---|
metadata: { "chave": "" } | String vazia remove aquela chave. |
knowledgeBases: [] | Desvincula todas as bases de conhecimento. |
mcpServers[].headers: {} | Apaga a credencial daquele servidor MCP. |
{
"version": 7,
"multiagent": null,
"defaults": null
}Campo omitido continua preservando. null só é aceito nos campos da primeira tabela.
#Duplicar agent
Duplicar cria um agent novo (nasce na v1, com histórico próprio) copiando a config corrente da origem. O corpo carrega só overrides opcionais de name e model — passar só model é o atalho para clonar o mesmo agent com outro modelo e comparar desempenho entre eles.
POST /v1/projects/:projectId/agents/:id/duplicate// Uma variante por modelo, a partir do mesmo agent base:
const haiku = await client.agents.duplicate(agent.id, {
name: 'Triagem — Haiku',
model: 'anthropic/claude-haiku-4-5',
});
const flash = await client.agents.duplicate(agent.id, {
name: 'Triagem — DeepSeek Flash',
model: 'deepseek/deepseek-v4-flash-0731',
});Pontos de contrato:
- A cópia é server-side. Fazer o mesmo com
GET+createclonaria os headers de servidor MCP já redigidos pela leitura; pelo endpoint, o clone nasce com a credencial vigente do armazém selado. nameausente vira<origem> (cópia). O nome não precisa ser único — renomeie depois se quiser.- Duplicar é uma escrita e passa pela mesma validação do create. Config da origem que não vale mais hoje (skill removida, base desvinculada, modelo do override incompatível) responde
422 agent.duplicate_invalidcom a causa no contexto. Falha de infraestrutura propaga com o código dela. - Origem arquivada duplica normalmente — o clone nasce ativo (é o jeito de reviver um agent arquivado).
#Restaurar uma versão
Restaurar copia a config de uma versão histórica e publica como nova versão corrente — o histórico nunca é reescrito.
POST /v1/projects/:projectId/agents/:id/versions/:version/restoreA versão de origem vai no path. O corpo carrega apenas a versão corrente esperada, para o lock otimista:
{ "version": 12 }const restored = await client.agents.restoreVersion(agent.id, {
fromVersion: 3,
version: agent.version,
});Pontos de contrato:
- A cópia é server-side. O corpo não carrega config. Isso importa porque a leitura redige o valor dos headers de servidor MCP: fazer o mesmo restore à mão (
GETcom?version=seguido deupdate) apagaria a credencial. Pelo endpoint de restore, os headers são preservados. - Credencial rotacionada não volta. Os headers vigentes do armazém selado são reaplicados sobre o snapshot restaurado. Restaurar a v3 depois de uma rotação na v7 mantém a credencial da v7 — restaurar config nunca quis dizer voltar credencial.
- Restaurar é uma escrita e passa pela mesma validação. Se a config antiga não vale mais no mundo de hoje (subagente do roster arquivado, base de conhecimento desvinculada, skill removida, ambiente ou cofre do padrão de execução apagado, modelo incompatível), a resposta é
422 agent.restore_invalide amessagenomeia a versão de origem e a causa. Falha de infraestrutura propaga com o código dela — o 422 significa "esta versão não vale mais", e repetir não resolve. - Restaurar a config que já é a corrente é no-op: devolve o agent sem publicar versão nova.
#Listar agents
A listagem devolve apenas agents ativos: um agent arquivado sai da lista. Use status para
mudar isso — active (padrão), archived (só os arquivados) ou all (os dois, útil para
auditoria e para resolver o nome de um agent arquivado a partir do id).
const ativos = await client.agents.list();
const arquivados = await client.agents.list({ status: 'archived' });
const todos = await client.agents.list({ status: 'all', name: 'atendimento' });O cursor de paginação vale para o conjunto de filtros que o gerou: ao trocar status (ou
name), recomece da primeira página.
#Padrões de execução
defaults define o ambiente, os cofres e os repositórios de memória que uma session deste agent usa quando quem cria a session não disse nada. Antes disso, cada chamada, automação e canal precisava repetir a mesma lista.
{
"defaults": {
"environmentId": "env_...",
"vaultIds": ["vault_..."],
"resources": [
{ "memoryStoreId": "ms_...", "memoryStoreName": "Procedimentos", "mode": "read-only" }
]
}
}| Campo | Tipo | Observação |
|---|---|---|
environmentId | string | Ambiente montado por padrão. Ausente = ambiente padrão do projeto. |
vaultIds | array | Até 16 cofres. Aceita id (vault_...) ou nome na escrita; a resposta traz sempre id. |
resources | array | Até 16 mounts de memória. memoryStoreId aceita id (ms_...) ou nome. |
memoryStoreName é obrigatório no corpo, mas o valor gravado vem sempre da store real — é um snapshot legível, nunca um nome divergente do id.
Precedência, aplicada na criação da session (nunca em runtime):
explícito da chamada > default do agent > default da contaO sinal de "explícito" é a presença do campo, não o conteúdo. vaultIds: [] é uma afirmação ("esta session roda sem cofre") e vence o default do agent; campo ausente é silêncio e cai no default. Para environmentId, cuja forma vazia não é representável, o explícito vazio é environmentId: null (ambiente base da conta).
Mudar defaults não afeta sessions que já existem: uma session é autocontida depois de nascer.
Os ponteiros são validados na escrita — ambiente inexistente ou arquivado, cofre ou store que não existem no escopo devolvem 422 com o código correspondente na tabela de erros. Recusar aqui evita um padrão quebrado que só falharia meses depois, num disparo sem dono.
#Permissões por tool
toolPermissions mapeia nome de tool → política. Ele é um override do padrão por origem:
| Origem da tool | Padrão |
|---|---|
| Built-in / server-side | always_allow |
| Tool de servidor MCP | always_ask |
| Política | Comportamento |
|---|---|
always_allow | A plataforma executa a tool quando o modelo pedir. |
always_ask | O loop pausa: a session vai para idle com stopReason igual a requires_action e aguarda um evento user.tool_confirmation. |
{
"toolPermissions": {
"web_fetch": "always_allow",
"browser_login": "always_ask",
"billing__create_invoice": "always_ask"
}
}Não existe deny: a política decide quando pedir confirmação, não o que remover do toolset (remover é editar tools). A chave é validada só de forma — nomear uma tool que o agent não oferece é inofensivo, e o namespacing de MCP (<servidor>__<tool>) vale aqui também. A mesma política é editável no console, na tela do agent.
Uma tool always_ask que ninguém confirma pendura a session em requires_action até alguém responder. Em execuções sem operador — um caso de experimento, ou um subagente em delegação foreground — isso vira erro explícito, na hora, em vez de espera.
#Multi-agente (coordenador e roster)
Um agent com multiagent.type: "coordinator" delega a um roster de outros agents. Cada subagente roda como uma session filha, com contexto próprio, e o modelo enxerga uma tool delegate_to_<role> por entrada.
{
"multiagent": {
"type": "coordinator",
"roster": [
{
"agentId": "agent_...",
"version": 4,
"role": "pesquisador",
"mode": "background",
"readonly": true,
"model": "anthropic/claude-haiku-4-5"
}
],
"budget": { "maxDelegations": 12 }
}
}| Campo | Tipo | Observação |
|---|---|---|
agentId | string | Agent alvo. Não pode ser o próprio coordenador nem outro coordenador (profundidade 1). |
version | number | Versão pinada. Omitida, a API resolve a corrente na escrita e grava o número. |
role | string | Handle único, kebab-case, até 48 caracteres. Vira o nome da tool delegate_to_<role>. |
mode | string | foreground (o coordenador espera) ou background (dispara e coleta depois). |
readonly | boolean | Confina o subagente a tools de leitura, em todas as origens. |
model | string ou objeto | Override do modelo do subagente. Substitui por inteiro; ausente = o modelo do próprio agent alvo. |
outputSchema | object | JSON Schema do output esperado, injetado na descrição da tool de delegação. |
Limite de 20 entradas por roster; budget.maxDelegations é o teto de spawns na árvore de delegação — estourado, o coordenador recusa novas delegações e conclui com o que já tem.
Política de arquivado. Arquivar um subagente impede vínculos novos (a validação de roster recusa com 422 agent.invalid_roster), mas não interrompe o que já está pinado: o coordenador delega para uma versão congelada, e ela continua existindo. Um coordenador em execução não cai por causa de uma decisão administrativa tomada em outra aba.
Como consequência, editar um coordenador só re-valida o roster quando o multiagent muda. Renomear o coordenador ou trocar o prompt não vira 422 por causa de um subagente arquivado sem relação com a edição. Restore de versão é a exceção: ele valida o roster por inteiro.
#Servidores MCP e rotação de headers
mcpServers declara servidores MCP remotos. O name é o namespace das tools expostas ao modelo (<name>__<toolName>): precisa ser único e não pode conter __.
O valor de headers é write-only. A leitura (GET, listagens, histórico de versões) devolve apenas os nomes das chaves, em headerKeys:
{
"mcpServers": [
{ "name": "billing", "url": "https://mcp.example/v1", "headerKeys": ["Authorization"] }
]
}Para trocar uma credencial, use a rotação — não o update:
PUT /v1/projects/:projectId/agents/:id/mcp-servers/:name/headers{ "headers": { "Authorization": "Bearer novo-token" } }const rotated = await client.agents.rotateMcpHeaders(agent.id, 'billing', {
Authorization: 'Bearer novo-token',
});Resposta:
{
"agentId": "agent_...",
"serverName": "billing",
"version": 12,
"headerKeys": ["Authorization"]
}Contrato da rotação:
- Não publica versão nova.
versionna resposta é a corrente, inalterada. Trocar umAuthorizationporupdategravaria o segredo novo no histórico de versões, para sempre. - Vale imediatamente, inclusive para sessions já em execução: a credencial é resolvida na hora de conectar ao servidor MCP.
- Merge por nome: header ausente do corpo é preservado. É o que permite trocar uma credencial sem conhecer as outras — e ninguém consegue lê-las.
- Exige ao menos um header e exige que o servidor exista na config corrente (
404 agent.mcp_server_not_found) — um nome com typo criaria uma credencial órfã que nunca seria usada nem notada. - Remover um header continua sendo edição da config:
updatecom o conjunto novo emmcpServers[].headers(o objeto declara o conjunto inteiro;{}limpa).
#Campos principais
| Campo | Tipo | Observação |
|---|---|---|
name | string | Nome de 1 a 200 caracteres. |
description | string | Opcional, para humanos (até 2000 caracteres). |
model | string ou objeto | Identifica provedor e modelo. |
system | string | Instruções persistentes. |
tools | array | Built-ins por string ou custom tools serializadas. Até 128, nomes únicos. |
mcpServers | array | Servidores MCP com name, url e headers opcionais (write-only). |
toolPermissions | object | always_allow ou always_ask por tool. |
skills | array | Referências pinadas por versão. Até 64. |
knowledgeBases | array | Allowlist de bases (kb_...) acessíveis ao agent. Até 20; ausente = nenhuma. |
serverlessTools | array | Tools serverless referenciadas com pin de versão. Até 64. |
egressPolicy | object | Override da política de egress; faz merge com a do workspace, sem relaxá-la. |
defaults | object | Ambiente, cofres e memória padrão da execução. |
multiagent | object | Coordenador e roster de subagentes. |
browser | object | Onde o navegador roda: self_hosted (padrão), steel_cloud ou browserbase. |
recordBrowser | boolean | Grava a navegação para replay. Ausente = desligado. |
tts | object | Voz do reply de áudio no WhatsApp. |
metadata | object | Pares string → string. |
A resposta acrescenta id, version, createdAt, updatedAt e archivedAt aos campos da config.
#Erros
| Código | HTTP | Quando |
|---|---|---|
agent.not_found | 404 | Agent inexistente ou fora do projeto. |
agent.version_not_found | 404 | ?version= (ou a origem do restore) aponta para versão que não existe. |
agent.version_conflict | 409 | A version enviada não é mais a corrente (alguém gravou no meio). |
agent.archived | 409 | Escrita em agent arquivado. |
agent.restore_invalid | 422 | A config da versão de origem não é mais aplicável. details.cause traz o porquê. |
agent.invalid_roster | 422 | Entrada do roster inexistente, arquivada, auto-referente ou já coordenadora. |
agent.unknown_knowledge_base | 422 | knowledgeBases referencia base fora do escopo. |
agent.environment_not_found | 422 | defaults.environmentId não existe no projeto, ou está arquivado. |
agent.vault_not_found | 422 | defaults.vaultIds referencia cofre que não existe no workspace. |
agent.memory_store_not_found | 422 | defaults.resources referencia store que não existe no projeto. |
agent.mcp_server_not_found | 404 | Rotação para um servidor MCP não declarado na config corrente. |
serverless.tool_not_found | 422 | serverlessTools referencia tool inexistente. |