{"openapi":"3.1.0","info":{"title":"managed-agents","description":"Run autonomous agent loops modeled after Anthropic's Managed Agents API. Agents (versioned configs) + Sessions (runtime instances) + Events (bidirectional stream). Auth: Hydra-issued OAuth2 access token (RS256, RFC 9068, aud=agent-api), plus Supabase JWTs when SUPABASE_JWT_SECRET is configured.","license":{"name":""},"version":"0.1.0"},"paths":{"/v1/agents":{"get":{"tags":["Agents"],"operationId":"list_agents","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":["string","null"]}}],"responses":{"200":{"description":"The caller's agents (optionally narrowed to one org)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentList"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Agents"],"operationId":"create_agent","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentRequest"}}},"required":true},"responses":{"201":{"description":"Agent created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentResponse"}}}},"400":{"description":"Invalid model / tool / reasoning_effort","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/agents/{id}":{"get":{"tags":["Agents"],"operationId":"get_agent","parameters":[{"name":"id","in":"path","description":"Agent id (e.g. agent_abc)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Latest version of the agent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentResponse"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Agent not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"patch":{"tags":["Agents"],"operationId":"patch_agent","parameters":[{"name":"id","in":"path","description":"Agent id (e.g. agent_abc)","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentPatch"}}},"required":true},"responses":{"200":{"description":"Agent after patch (version unchanged on no-op)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentResponse"}}}},"400":{"description":"Invalid model / tool / reasoning_effort","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Agent not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Version mismatch (optimistic concurrency)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/agents/{id}/archive":{"post":{"tags":["Agents"],"operationId":"archive_agent","parameters":[{"name":"id","in":"path","description":"Agent id (e.g. agent_abc)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Agent archived (idempotent)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentResponse"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Agent not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/agents/{id}/mcp-tools":{"get":{"tags":["Agents"],"operationId":"list_agent_mcp_tools","parameters":[{"name":"id","in":"path","description":"Agent id (e.g. agent_abc)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Live per-server tool discovery (unfiltered catalog or connect error)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpToolsResponse"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Agent not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/agents/{id}/versions":{"get":{"tags":["Agents"],"operationId":"list_agent_versions","parameters":[{"name":"id","in":"path","description":"Agent id (e.g. agent_abc)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Every persisted version, oldest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentList"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Agent not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/build-cli/sessions":{"post":{"tags":["Build CLI"],"operationId":"create_session","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBuildCliSessionRequest"}}},"required":true},"responses":{"200":{"description":"Existing active session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"201":{"description":"Build CLI session created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"400":{"description":"Invalid local session UUID or model","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Caller is not the Build CLI OAuth client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"The matching session is archived","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/build-cli/sessions/local/{local_session_id}":{"get":{"tags":["Build CLI"],"operationId":"get_session_by_local_id","parameters":[{"name":"local_session_id","in":"path","description":"Canonical UUIDv4 from local JSONL metadata","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Active Build CLI session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"400":{"description":"Invalid local session UUID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Caller is not the Build CLI OAuth client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No matching session owned by the caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"The matching session is archived","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/build-cli/sessions/{id}/archive":{"post":{"tags":["Build CLI"],"operationId":"archive_session","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Session archived and worker revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/build-cli/sessions/{id}/events":{"get":{"tags":["Build CLI"],"operationId":"list_events","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"after","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildCliEventPage"}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Build CLI"],"operationId":"append_controller_message","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildCliTextMessageRequest"}}},"required":true},"responses":{"202":{"description":"Controller message durably queued"},"409":{"description":"No ready worker is attached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/build-cli/sessions/{id}/events/stream":{"get":{"tags":["Build CLI"],"operationId":"stream_events","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"after","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Bounded replay followed by live durable events","content":{"text/event-stream":{}}},"409":{"description":"Replay exceeds one page; catch up through event history first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/build-cli/sessions/{id}/interrupt":{"post":{"tags":["Build CLI"],"operationId":"interrupt_session","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Interrupt durably queued"},"409":{"description":"No ready worker is attached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/build-cli/sessions/{id}/worker":{"get":{"tags":["Build CLI"],"operationId":"get_worker","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildCliWorkerResponse"}}}},"404":{"description":"Session not found for this owner","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Session is archived","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Build CLI"],"operationId":"attach_worker","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachBuildCliWorkerRequest"}}},"required":true},"responses":{"200":{"description":"Worker attached; any older worker is fenced","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachBuildCliWorkerResponse"}}}},"403":{"description":"Caller is not the Build CLI OAuth client","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Session not found for this owner","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Session is archived","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/build-cli/sessions/{id}/worker/detach":{"post":{"tags":["Build CLI"],"operationId":"detach_worker","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Cluster-Worker-Token","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildCliWorkerEpochRequest"}}},"required":true},"responses":{"202":{"description":"Worker detached"},"403":{"description":"Invalid worker capability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Worker was superseded, expired, or session is archived","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/build-cli/sessions/{id}/worker/events":{"post":{"tags":["Build CLI"],"operationId":"append_worker_event","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Cluster-Worker-Token","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppendBuildCliWorkerEventRequest"}}},"required":true},"responses":{"202":{"description":"Worker event durably appended"},"403":{"description":"Invalid worker capability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Worker was superseded or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/build-cli/sessions/{id}/worker/heartbeat":{"post":{"tags":["Build CLI"],"operationId":"heartbeat_worker","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Cluster-Worker-Token","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildCliWorkerEpochRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildCliWorkerResponse"}}}},"403":{"description":"Invalid worker capability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Worker was superseded, expired, or session is archived","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/deployments":{"get":{"tags":["Deployments"],"operationId":"list_deployments","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":["string","null"]}}],"responses":{"200":{"description":"Deployments owned by the caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeploymentList"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Deployments"],"operationId":"create_deployment","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDeploymentRequest"}}},"required":true},"responses":{"201":{"description":"Deployment created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deployment"}}}},"400":{"description":"Invalid environment template_id / repos, or too many vault_ids","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"The agent (or pinned version), bound environment, or a vault does not exist / isn't owned by the caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/deployments/{id}":{"get":{"tags":["Deployments"],"operationId":"get_deployment","parameters":[{"name":"id","in":"path","description":"Deployment id (e.g. depl_abc)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deployment record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deployment"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Deployment not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Deployments"],"operationId":"delete_deployment","parameters":[{"name":"id","in":"path","description":"Deployment id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deployment deleted (its sessions' run history survives)"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Deployment not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"patch":{"tags":["Deployments"],"operationId":"patch_deployment","parameters":[{"name":"id","in":"path","description":"Deployment id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchDeploymentRequest"}}},"required":true},"responses":{"200":{"description":"Deployment updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Deployment"}}}},"400":{"description":"Invalid environment template_id / repos, or too many vault_ids","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Deployment — or a newly-referenced agent/environment/vault — not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/deployments/{id}/run":{"post":{"tags":["Deployments"],"operationId":"run_deployment","parameters":[{"name":"id","in":"path","description":"Deployment id","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Run started; the session is owned by the caller and carries deployment_id. Stream it at /v1/sessions/{id}/events/stream.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"400":{"description":"Missing bearer, a repo that failed to clone, or (Ephemeral) a bearer too close to expiry to complete + tear down the run","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Existing/Ephemeral deployment runs require the compute:use scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Deployment — or its agent / since-deleted environment / vault — not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"The bound (Existing) environment is archived","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"502":{"description":"VM provisioning failed (infra) or the agent run could not start","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/deployments/{id}/runs":{"get":{"tags":["Deployments"],"operationId":"list_deployment_runs","parameters":[{"name":"id","in":"path","description":"Deployment id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The deployment's runs (its sessions, newest-last)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionList"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Deployment not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/environments":{"get":{"tags":["Environments"],"operationId":"list_environments","responses":{"200":{"description":"Environments owned by the caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnvironmentList"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Environments"],"operationId":"create_environment","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEnvironmentRequest"}}},"required":true},"responses":{"201":{"description":"Environment created (no VM until first bind)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnvironmentResponse"}}}},"400":{"description":"Unsupported config (e.g. networking.type 'limited')","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"An environment with this name already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/environments/{id}":{"get":{"tags":["Environments"],"operationId":"get_environment","parameters":[{"name":"id","in":"path","description":"Environment id (e.g. env_abc)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Environment record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnvironmentResponse"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Environment not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Environments"],"operationId":"delete_environment","parameters":[{"name":"id","in":"path","description":"Environment id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Environment deleted; backing VM teardown is best-effort"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Environment not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/environments/{id}/archive":{"post":{"tags":["Environments"],"operationId":"archive_environment","parameters":[{"name":"id","in":"path","description":"Environment id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Environment archived; backing VM teardown is best-effort (idempotent)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnvironmentResponse"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Environment not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/hooks/github":{"post":{"tags":["Triggers"],"operationId":"github_hook","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NormalizedHookEvent"}}},"required":true},"responses":{"200":{"description":"Nothing new — all duplicates, or no matching trigger","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HookAck"}}}},"202":{"description":"At least one fire accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HookAck"}}}},"400":{"description":"Malformed normalized event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Caller lacks managed-agents VM provisioning scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Caller is not the trigger machine identity (or triggers are unconfigured)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/mcp-services":{"get":{"tags":["MCP Services"],"operationId":"list_mcp_services","responses":{"200":{"description":"Curated hosted MCP services catalog","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpServiceList"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/mcp-services/connect/complete":{"post":{"tags":["MCP Services"],"operationId":"complete_mcp_service_connect","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpServiceConnectCompleteRequest"}}},"required":true},"responses":{"200":{"description":"OAuth connect completion result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpServiceConnectCompleteResponse"}}}},"400":{"description":"Invalid or expired OAuth state/code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"502":{"description":"OAuth provider token exchange failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/mcp-services/{id}/connect/start":{"post":{"tags":["MCP Services"],"operationId":"start_mcp_service_connect","parameters":[{"name":"id","in":"path","description":"Catalog MCP service id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpServiceConnectStartRequest"}}},"required":true},"responses":{"200":{"description":"OAuth authorization URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpServiceConnectStartResponse"}}}},"400":{"description":"Unknown service, invalid return URL, or provider OAuth metadata unsupported","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"502":{"description":"OAuth provider discovery or registration failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/mcp-services/{id}/connection":{"delete":{"tags":["MCP Services"],"operationId":"disconnect_mcp_service","parameters":[{"name":"id","in":"path","description":"Catalog MCP service id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Connection removed if present"},"400":{"description":"Unknown service","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/mcp-services/{id}/tool-selection":{"put":{"tags":["MCP Services"],"operationId":"put_mcp_service_tool_selection","parameters":[{"name":"id","in":"path","description":"Catalog MCP service id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpServiceToolSelectionRequest"}}},"required":true},"responses":{"200":{"description":"Saved hosted MCP service disabled-tool selection","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpServiceToolSelectionResponse"}}}},"400":{"description":"Unknown service or invalid tool selection","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/mcp-services/{id}/tools":{"get":{"tags":["MCP Services"],"operationId":"list_mcp_service_tools","parameters":[{"name":"id","in":"path","description":"Catalog MCP service id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Live hosted MCP service tool catalog with user selection state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/McpServiceToolList"}}}},"400":{"description":"Unknown service, disconnected service, or MCP service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/models":{"get":{"tags":["Models"],"operationId":"list_models","responses":{"200":{"description":"Supported models","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelsResponse"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"502":{"description":"Supported model missing from Catwalk registry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/runs":{"post":{"tags":["Runs"],"operationId":"create_run","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRunRequest"}}},"required":true},"responses":{"202":{"description":"Run accepted; provisioned + started. Stream the session for results.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRunResponse"}}}},"400":{"description":"Bad request: not exactly one of mode|agent|builtin, empty repos for mode/builtin (allowed for agent), invalid repos or template_id, mode without pr_number/kickoff, builtin without request (or builtin with kickoff), HTTP-only agent, a repo that failed to clone, or a bearer too close to expiry to complete + tear down the run (refresh the token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"VM-backed runs require the compute:use scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"The `agent` is unknown or owned by another user, or the `builtin` slug is unknown, not publicly runnable, or not available in this deployment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"502":{"description":"VM provisioning failed (infra) or the agent run could not start","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/schedules":{"get":{"tags":["Schedules"],"operationId":"list_schedules","parameters":[{"name":"agent_id","in":"query","description":"Filter to one agent","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"The caller's schedules","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleList"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Schedules"],"operationId":"create_schedule","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateScheduleRequest"}}},"required":true},"responses":{"201":{"description":"Schedule created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Schedule"}}}},"400":{"description":"Unknown agent or invalid cron/timezone","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/schedules/{id}":{"get":{"tags":["Schedules"],"operationId":"get_schedule","parameters":[{"name":"id","in":"path","description":"Schedule id (sched_...)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The schedule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Schedule"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Not found (or owned by another user)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Schedules"],"operationId":"delete_schedule","parameters":[{"name":"id","in":"path","description":"Schedule id (sched_...)","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Not found (or owned by another user)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"patch":{"tags":["Schedules"],"operationId":"patch_schedule","parameters":[{"name":"id","in":"path","description":"Schedule id (sched_...)","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SchedulePatch"}}},"required":true},"responses":{"200":{"description":"The updated schedule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Schedule"}}}},"400":{"description":"Invalid cron/timezone","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Not found (or owned by another user)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/sessions":{"get":{"tags":["Sessions"],"operationId":"list_sessions","parameters":[{"name":"agent_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"deployment_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"profile","in":"query","required":false,"schema":{"$ref":"#/components/schemas/SessionProfile"}},{"name":"surface","in":"query","description":"Product grouping across profiles. `build` includes both hosted\nworkspace sessions and local Build CLI sessions unless `profile`\nfurther narrows the result.","required":false,"schema":{"$ref":"#/components/schemas/WorkspaceSurface"}},{"name":"project_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"include_archived","in":"query","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Sessions matching the filter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionList"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Sessions"],"operationId":"create_session","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSessionRequest"}}},"required":true},"responses":{"201":{"description":"Session created (idle)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"400":{"description":"Invalid agent reference / version pin; or the caller's bearer was rejected by infra during on-behalf-of VM provisioning (refresh it and retry)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Environment binding requires the compute:use scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Agent — or the bound environment — does not exist (or isn't owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"The bound environment is archived","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"502":{"description":"Environment binding requested but VM provisioning is not configured / infra failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/sessions/{id}":{"get":{"tags":["Sessions"],"operationId":"get_session","parameters":[{"name":"id","in":"path","description":"Session id (e.g. sesn_abc)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Session record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Session not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Sessions"],"operationId":"delete_session","parameters":[{"name":"id","in":"path","description":"Session id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Session and its event log deleted"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Session not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Session is currently running; interrupt before delete","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"patch":{"tags":["Sessions"],"operationId":"patch_session","parameters":[{"name":"id","in":"path","description":"Session id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchSessionRequest"}}},"required":true},"responses":{"200":{"description":"Session updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Session not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Session is currently running; interrupt before archiving","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/sessions/{id}/archive":{"post":{"tags":["Sessions"],"operationId":"archive_session","parameters":[{"name":"id","in":"path","description":"Session id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Session archived (idempotent)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Session"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Session not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Session is currently running; interrupt before archive","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/sessions/{id}/attach-folder":{"post":{"tags":["Sessions"],"operationId":"attach_folder","parameters":[{"name":"id","in":"path","description":"Session id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Hidden attachment folder created and mounted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachFolderResponse"}}}},"400":{"description":"Not a workspace session / bearer missing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Session not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Turn running, folder already attached, or non-attachable environment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/sessions/{id}/chat":{"post":{"tags":["Sessions"],"operationId":"chat","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionChatRequest"}}},"required":true},"responses":{"200":{"description":"AI SDK UI Message Stream","content":{"text/event-stream":{"schema":{"$ref":"#/components/schemas/AiSdkChunk"}}}},"400":{"description":"Invalid message or model","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"402":{"description":"Credits exhausted or paid workspace plan required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Workspace chat requires the compute:use scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Session not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Session is archived or already running","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/sessions/{id}/chat/stream":{"get":{"tags":["Sessions"],"operationId":"resume_chat","parameters":[{"name":"id","in":"path","description":"Session id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Full current-turn replay followed by its live AI SDK tail","content":{"text/event-stream":{"schema":{"$ref":"#/components/schemas/AiSdkChunk"}}}},"204":{"description":"Nothing resumable for this caller"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Workspace chat requires the compute:use scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/sessions/{id}/events":{"get":{"tags":["Events"],"operationId":"list_events","parameters":[{"name":"id","in":"path","description":"Session id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Full event log in append order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventList"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Session not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Events"],"operationId":"append_events","parameters":[{"name":"id","in":"path","description":"Session id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppendEventsRequest"}}},"required":true},"responses":{"202":{"description":"Events accepted; the resulting agent turn (if any) is in flight"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Session not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Session is already running, or its profile requires a dedicated input endpoint","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/sessions/{id}/events/stream":{"get":{"tags":["Events"],"operationId":"stream_events","parameters":[{"name":"id","in":"path","description":"Session id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"SSE stream of events as they happen","content":{"text/event-stream":{}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Session not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/sessions/{id}/interrupt":{"post":{"tags":["Sessions"],"operationId":"interrupt","parameters":[{"name":"id","in":"path","description":"Session id","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Interrupt accepted"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Session not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/sessions/{id}/messages":{"get":{"tags":["Sessions"],"operationId":"list_messages","parameters":[{"name":"id","in":"path","description":"Session id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Session history projected as AI SDK UIMessage[]. While a turn is running, its partial output is omitted — GET /chat/stream replays the live turn in full.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UiMessage"}}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Session not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/tools":{"get":{"tags":["Tools"],"operationId":"list_tools","responses":{"200":{"description":"All registered tools, sorted by name","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolList"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/triggers":{"get":{"tags":["Triggers"],"operationId":"list_triggers","parameters":[{"name":"agent_id","in":"query","description":"Filter to one agent","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"The caller's triggers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TriggerList"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Triggers"],"operationId":"create_trigger","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTriggerRequest"}}},"required":true},"responses":{"201":{"description":"Trigger created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Trigger"}}}},"400":{"description":"Unknown agent, bad repo slug, or unsupported events","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/triggers/{id}":{"get":{"tags":["Triggers"],"operationId":"get_trigger","parameters":[{"name":"id","in":"path","description":"Trigger id (trg_...)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The trigger","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Trigger"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Not found (or owned by another user)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Triggers"],"operationId":"delete_trigger","parameters":[{"name":"id","in":"path","description":"Trigger id (trg_...)","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted (past fires survive with trigger_id nulled)"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Not found (or owned by another user)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"patch":{"tags":["Triggers"],"operationId":"patch_trigger","parameters":[{"name":"id","in":"path","description":"Trigger id (trg_...)","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TriggerPatch"}}},"required":true},"responses":{"200":{"description":"The updated trigger","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Trigger"}}}},"400":{"description":"Bad repo slug or unsupported events","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Not found (or owned by another user)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/triggers/{id}/fires":{"get":{"tags":["Triggers"],"operationId":"list_trigger_fires","parameters":[{"name":"id","in":"path","description":"Trigger id (trg_...)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The trigger's fires, newest first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FireList"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Not found (or owned by another user)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/vaults":{"get":{"tags":["Vaults"],"operationId":"list_vaults","parameters":[{"name":"include_archived","in":"query","description":"Include archived vaults (default false)","required":false,"schema":{"type":"boolean"}},{"name":"limit","in":"query","description":"Max records to return, clamped to 1..=100 (default 100)","required":false,"schema":{"type":"integer","format":"int32","minimum":0}}],"responses":{"200":{"description":"Vaults owned by the caller (newest first)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VaultList"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Vaults"],"operationId":"create_vault","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVaultRequest"}}},"required":true},"responses":{"201":{"description":"Vault created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VaultResponse"}}}},"400":{"description":"Invalid display_name / metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/vaults/{id}":{"get":{"tags":["Vaults"],"operationId":"get_vault","parameters":[{"name":"id","in":"path","description":"Vault id (e.g. vlt_abc)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Vault record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VaultResponse"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Vault not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Vaults"],"operationId":"update_vault","parameters":[{"name":"id","in":"path","description":"Vault id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVaultRequest"}}},"required":true},"responses":{"200":{"description":"Vault updated (display_name / metadata)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VaultResponse"}}}},"400":{"description":"No updatable fields, or invalid display_name / metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Vault not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Vault is archived (terminal)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Vaults"],"operationId":"delete_vault","parameters":[{"name":"id","in":"path","description":"Vault id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Vault and all its credentials hard-deleted"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Vault not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/vaults/{id}/archive":{"post":{"tags":["Vaults"],"operationId":"archive_vault","parameters":[{"name":"id","in":"path","description":"Vault id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Vault archived; all active credentials' secrets purged (idempotent)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VaultResponse"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Vault not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/vaults/{id}/credentials":{"get":{"tags":["Vaults"],"operationId":"list_credentials","parameters":[{"name":"id","in":"path","description":"Vault id","required":true,"schema":{"type":"string"}},{"name":"include_archived","in":"query","description":"Include archived credentials (default false)","required":false,"schema":{"type":"boolean"}},{"name":"limit","in":"query","description":"Max records to return, clamped to 1..=100 (default 100)","required":false,"schema":{"type":"integer","format":"int32","minimum":0}}],"responses":{"200":{"description":"Credentials in the vault, newest first (tokens NEVER returned)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VaultCredentialList"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Vault not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Vaults"],"operationId":"create_credential","parameters":[{"name":"id","in":"path","description":"Vault id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCredentialRequest"}}},"required":true},"responses":{"201":{"description":"Credential created (token encrypted at rest; never echoed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VaultCredentialResponse"}}}},"400":{"description":"Invalid fields, or credential storage not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Vault not found (or not owned by the caller)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Duplicate active mcp_server_url, or the per-vault credential cap","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/vaults/{id}/credentials/{cid}":{"put":{"tags":["Vaults"],"operationId":"update_credential","parameters":[{"name":"id","in":"path","description":"Vault id","required":true,"schema":{"type":"string"}},{"name":"cid","in":"path","description":"Credential id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCredentialRequest"}}},"required":true},"responses":{"200":{"description":"Credential rotated/renamed (new token encrypted; never echoed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VaultCredentialResponse"}}}},"400":{"description":"No updatable fields, empty token, immutable mcp_server_url change, or storage not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Vault/credential not found (or not owned)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Credential is archived (terminal)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Vaults"],"operationId":"delete_credential","parameters":[{"name":"id","in":"path","description":"Vault id","required":true,"schema":{"type":"string"}},{"name":"cid","in":"path","description":"Credential id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Credential hard-deleted"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Vault/credential not found (or not owned)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/vaults/{id}/credentials/{cid}/archive":{"post":{"tags":["Vaults"],"operationId":"archive_credential","parameters":[{"name":"id","in":"path","description":"Vault id","required":true,"schema":{"type":"string"}},{"name":"cid","in":"path","description":"Credential id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Credential archived; secret purged, record kept","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VaultCredentialResponse"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Vault/credential not found (or not owned)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/vaults/{id}/credentials/{cid}/mcp_oauth_validate":{"post":{"tags":["Vaults"],"operationId":"mcp_oauth_validate","parameters":[{"name":"id","in":"path","description":"Vault id","required":true,"schema":{"type":"string"}},{"name":"cid","in":"path","description":"Credential id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Diagnose report (status valid/invalid/unknown). NOTE: a refresh attempt may rotate + persist the stored refresh token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CredentialValidation"}}}},"400":{"description":"Not an mcp_oauth credential","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Vault/credential not found (or not owned)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Credential is archived (terminal)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/webhooks":{"get":{"tags":["Webhooks"],"operationId":"list_webhooks","responses":{"200":{"description":"The caller's subscriptions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookList"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"502":{"description":"Webhooks not configured (DATABASE_URL / MCP_CREDENTIALS_KEY unset) or a store/crypto backend failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"post":{"tags":["Webhooks"],"operationId":"create_webhook","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookRequest"}}},"required":true},"responses":{"201":{"description":"Subscription created; the signing secret is returned ONCE","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreated"}}}},"400":{"description":"Invalid url or empty events","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"502":{"description":"Webhooks not configured (DATABASE_URL / MCP_CREDENTIALS_KEY unset) or a store/crypto backend failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}},"/v1/webhooks/{id}":{"get":{"tags":["Webhooks"],"operationId":"get_webhook","parameters":[{"name":"id","in":"path","description":"Subscription id (whk_...)","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The subscription","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponse"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Not found (or owned by another user)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"502":{"description":"Webhooks not configured (DATABASE_URL / MCP_CREDENTIALS_KEY unset) or a store/crypto backend failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["Webhooks"],"operationId":"delete_webhook","parameters":[{"name":"id","in":"path","description":"Subscription id (whk_...)","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Not found (or owned by another user)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"502":{"description":"Webhooks not configured (DATABASE_URL / MCP_CREDENTIALS_KEY unset) or a store/crypto backend failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]},"patch":{"tags":["Webhooks"],"operationId":"patch_webhook","parameters":[{"name":"id","in":"path","description":"Subscription id (whk_...)","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookPatch"}}},"required":true},"responses":{"200":{"description":"The updated subscription","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponse"}}}},"400":{"description":"Invalid url or empty events","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Not found (or owned by another user)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"502":{"description":"Webhooks not configured (DATABASE_URL / MCP_CREDENTIALS_KEY unset) or a store/crypto backend failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"bearerAuth":[]}]}}},"components":{"schemas":{"AgentEnvironment":{"type":"object","description":"Ephemeral-VM spec for VM-backed runs of an agent (#173). Carried on\n[`Agent::environment`]; when a run provisions a VM for this agent it\ndefaults the infra template + repos to clone from here.","properties":{"repos":{"type":"array","items":{"$ref":"#/components/schemas/GitRepo"},"description":"Repos to clone into the VM. Empty → a workspace VM with no clone (the\nnews-vm case). Normalized (SSH→HTTPS, etc.) at create/patch."},"template_id":{"type":["string","null"],"description":"infra VM template (`workspace` / `development` / `browser`). `None` →\ninfra's default. `workspace` bakes the skills repo into `/mnt/skills`;\n`browser` bakes Chrome + agent-browser for direct website interaction.\nValidated against the allowlist at create/patch."}}},"AgentList":{"type":"object","description":"`GET /v1/agents` response — projects each [`Agent`] through\n[`AgentResponse`] so `owner_sub` never reaches the wire.","required":["data","has_more"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AgentResponse"}},"has_more":{"type":"boolean","description":"Always `false` in v1 — the list endpoint returns every owned agent.\nReserved for cursor pagination when the catalog grows large."}}},"AgentMcpAuth":{"oneOf":[{"type":"object","description":"Unauthenticated (public servers).","required":["type"],"properties":{"type":{"type":"string","enum":["none"]}}},{"type":"object","description":"Mint a service bearer at connect time from a named provider — the\nfirst-party lane (e.g. our own gateway MCP server). No secret stored.","required":["provider","type"],"properties":{"provider":{"$ref":"#/components/schemas/TokenSourceProvider"},"type":{"type":"string","enum":["token_source"]}}}],"description":"Agent-level MCP auth. Deliberately a SUBSET of the transport crate's\n`ServerAuth` — no static bearer here (that's a vault credential, M4); an\nagent definition never carries a secret."},"AgentPatch":{"type":"object","description":"PATCH body. All fields optional; only present fields update.\n\nPer the Managed Agents spec:\n- Scalar fields (`model`, `system`, `name`) replace. `system` /\n  `reasoning_effort` can additionally be cleared by sending `null`\n  (see [`PatchField`]).\n- Array fields (`tools`) fully replace.\n- Metadata merges at the key level (empty-string value deletes a key); send\n  `null` to clear ALL keys (an empty object `{}` is a no-op merge, not a\n  clear — see [`AgentPatch::metadata`]).\n- `version` is the client's view of the agent; the server rejects the\n  patch (409) if the persisted version has moved on.\n\nThe schema representation marks each `PatchField` field as a nullable\noptional of the inner type — `OpenAPI` doesn't distinguish \"field\nabsent\" from \"field present with value `null`\", so clients see a\n`T | null` field they can omit to preserve, send `null` to clear, or\nsend a value to replace. The description on each field carries the\ntri-state meaning.","required":["version"],"properties":{"environment":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AgentEnvironment","description":"Ephemeral-VM spec (#173). Omit to preserve, send `null` to clear, send\nan object to replace. Re-validated post-patch (template allowlist +\nrepo normalization)."}]},"mcp_servers":{"type":["array","null"],"items":{"$ref":"#/components/schemas/McpServerConfig"},"description":"MCP servers — array full-replace (like `tools`). Omit to preserve.\nRe-validated post-patch (name/url/SSRF/caps)."},"metadata":{"type":["object","null"],"description":"Metadata. **Omit** to preserve; send **`null`** to clear ALL keys; send\nan **object** to merge at the key level (empty-string value deletes that\nkey, non-empty sets it). An empty object **`{}`** is a no-op merge, NOT a\nclear — the merge semantics make `{}` mean \"change nothing\", so `null` is\nthe explicit wipe signal (the wire spec doesn't define `{}`-clears).","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"model":{"type":["string","null"]},"name":{"type":["string","null"]},"org_id":{"type":["string","null"],"description":"Org filing. Omit to preserve, send `null` to unfile, send a string\nto (re)file the agent under that org."},"reasoning_effort":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ReasoningEffort","description":"Reasoning effort. Omit to preserve, send `null` to clear, send a\nlevel to replace."}]},"system":{"type":["string","null"],"description":"`system` prompt. Omit to preserve, send `null` to clear, send a\nstring to replace."},"tools":{"type":["array","null"],"items":{"$ref":"#/components/schemas/ToolRef"}},"version":{"type":"integer","format":"int32","minimum":0}}},"AgentRef":{"oneOf":[{"type":"string","description":"Pin to the latest version of the agent at session-create time."},{"type":"object","description":"Pin to a specific version. Useful when the agent might be\n`PATCH`ed mid-flight and the session should not pick up the change.","required":["id","version"],"properties":{"id":{"type":"string"},"version":{"type":"integer","format":"int32","minimum":0}}}],"description":"Either a bare agent id (latest version implied) or an explicit\n`{id, version}` pin."},"AgentResponse":{"type":"object","description":"Agent response shape — projects [`Agent`], omitting `owner_sub`.\n\nEvery read is already owner-scoped, so the owner is the caller by\nconstruction; mirrors [`EnvironmentResponse`] hiding `owner`.","required":["id","name","model","tools","metadata","version","created_at","updated_at"],"properties":{"archived_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"environment":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AgentEnvironment","description":"Ephemeral-VM spec (#173). Omitted when the agent has none."}]},"id":{"type":"string"},"mcp_servers":{"type":"array","items":{"$ref":"#/components/schemas/McpServerConfig"},"description":"Declared MCP servers (#142). Omitted when none. Secret-free —\n`token_source` auth carries a provider name, never a credential."},"metadata":{"type":"object","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"model":{"type":"string"},"name":{"type":"string"},"org_id":{"type":["string","null"],"description":"Org the agent is filed under. Omitted when unfiled."},"reasoning_effort":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ReasoningEffort"}]},"system":{"type":["string","null"]},"tools":{"type":"array","items":{"$ref":"#/components/schemas/ToolRef"}},"updated_at":{"type":"string","format":"date-time"},"version":{"type":"integer","format":"int32","minimum":0}}},"AiSdkChunk":{"oneOf":[{"type":"object","description":"First chunk. `messageId` is supplied by the caller; llm-gateway uses a\n`msg_` prefix, while persisted chat-api uses the gateway row UUID.","required":["messageId","type"],"properties":{"messageId":{"type":"string"},"type":{"type":"string","enum":["start"]}}},{"type":"object","description":"Transient managed workspace lifecycle signal. The AI SDK accepts\n`data-*` chunks and forwards `transient: true` chunks to `onData`\nwithout adding them to message history.","required":["data","transient","type"],"properties":{"data":{"type":"object"},"id":{"type":["string","null"]},"transient":{"type":"boolean"},"type":{"type":"string","enum":["data-workspace-lifecycle"]}}},{"type":"object","description":"Opens the (single, in v1) model step.","required":["type"],"properties":{"type":{"type":"string","enum":["start-step"]}}},{"type":"object","required":["id","type"],"properties":{"id":{"type":"string"},"type":{"type":"string","enum":["text-start"]}}},{"type":"object","required":["id","delta","type"],"properties":{"delta":{"type":"string"},"id":{"type":"string"},"type":{"type":"string","enum":["text-delta"]}}},{"type":"object","required":["id","type"],"properties":{"id":{"type":"string"},"type":{"type":"string","enum":["text-end"]}}},{"type":"object","required":["id","type"],"properties":{"id":{"type":"string"},"type":{"type":"string","enum":["reasoning-start"]}}},{"type":"object","required":["id","delta","type"],"properties":{"delta":{"type":"string"},"id":{"type":"string"},"type":{"type":"string","enum":["reasoning-delta"]}}},{"type":"object","description":"Carries the replay payload at block close. `providerMetadata` is namespaced\n`{anthropic: {signature?, redactedData?}}` or\n`{openai: {itemId?, reasoningEncryptedContent?}}` — the same keys the\nadapter parses back inbound.","required":["id","type"],"properties":{"id":{"type":"string"},"providerMetadata":{"type":"object"},"type":{"type":"string","enum":["reasoning-end"]}}},{"type":"object","description":"`providerExecuted: true` on a server-executed tool's **input** chunks is\nload-bearing, not cosmetic: the AI SDK client decides whether to\ndispatch `onToolCall` (local execution) when it processes\n`tool-input-available` — before any output chunk arrives — gated on\n`!chunk.providerExecuted`. A gateway-run tool whose inputs omit the flag\ngets executed a second time by a contract-following client. Client-local\nsurfaces omit it (`None`); `tool-input-delta` has no such field in the\nSDK schema.","required":["toolCallId","toolName","type"],"properties":{"providerExecuted":{"type":["boolean","null"]},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"type":{"type":"string","enum":["tool-input-start"]}}},{"type":"object","required":["toolCallId","inputTextDelta","type"],"properties":{"inputTextDelta":{"type":"string"},"toolCallId":{"type":"string"},"type":{"type":"string","enum":["tool-input-delta"]}}},{"type":"object","required":["toolCallId","toolName","input","type"],"properties":{"input":{"type":"object"},"providerExecuted":{"type":["boolean","null"]},"toolCallId":{"type":"string"},"toolName":{"type":"string"},"type":{"type":"string","enum":["tool-input-available"]}}},{"type":"object","description":"Server-executed tool result (the #107 server-side loop). Pairs with a\nprior `tool-input-available` of the same `toolCallId`. `providerExecuted`\nis `true` for gateway-run tools so `useChat` renders the result rather than\ndispatching the call back to the client to run.","required":["toolCallId","output","type"],"properties":{"output":{"type":"object"},"providerExecuted":{"type":["boolean","null"]},"toolCallId":{"type":"string"},"type":{"type":"string","enum":["tool-output-available"]}}},{"type":"object","description":"Server-executed tool failure — the error-shaped sibling of\n`tool-output-available` (same `toolCallId` pairing, `errorText` in place of\n`output`).","required":["toolCallId","errorText","type"],"properties":{"errorText":{"type":"string"},"providerExecuted":{"type":["boolean","null"]},"toolCallId":{"type":"string"},"type":{"type":"string","enum":["tool-output-error"]}}},{"type":"object","description":"Closes the step.","required":["type"],"properties":{"type":{"type":"string","enum":["finish-step"]}}},{"type":"object","description":"App-defined metadata; carries `{usage: {inputTokens, outputTokens, totalTokens}}`\nwhen `stream_options.include_usage` is set.","required":["messageMetadata","type"],"properties":{"messageMetadata":{"type":"object"},"type":{"type":"string","enum":["message-metadata"]}}},{"type":"object","description":"Terminal logical chunk (followed by `data: [DONE]`).","required":["type"],"properties":{"type":{"type":"string","enum":["finish"]}}},{"type":"object","description":"Mid-stream upstream error; replaces `finish`. `[DONE]` still follows.","required":["errorText","type"],"properties":{"errorText":{"type":"string"},"type":{"type":"string","enum":["error"]}}}],"description":"A single chunk on the UI Message Stream, serialized as\n`{\"type\": \"<kebab-case>\", ...}`.\n\nVariant set + field names are the subset of the AI SDK `UIMessageChunk`\nunion this gateway emits. The `tool-output-*` pair is protocol vocabulary for\nthe #107 server-side tool loop (which executes a tool and streams its result\nback as `providerExecuted: true`); llm-gateway's v1 `/v1/chat` is single-step,\nclient-side tools only, so it emits `tool-input-*` but never these. Client-side\nvalidation is a zod `strictObject`: do not add fields here without confirming\nthey exist on the corresponding chunk."},"AppendBuildCliWorkerEventRequest":{"type":"object","required":["worker_epoch","event"],"properties":{"event":{"$ref":"#/components/schemas/BuildCliWorkerEvent"},"worker_epoch":{"type":"integer","format":"int64","minimum":0}}},"AppendEventsRequest":{"type":"object","description":"POST `/v1/sessions/:id/events` request body.\n\nAnthropic batches client events as `{events: [...]}` so multiple\nrelated user events (e.g. `user.message` + a follow-up\n`user.interrupt`) can be submitted atomically. v1 supports the two\nuser-driven event types — `user.message` and `user.interrupt`.","required":["events"],"properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/UserInputEvent"}}}},"AttachBuildCliWorkerRequest":{"type":"object","properties":{"cli_version":{"type":["string","null"]},"cwd":{"type":["string","null"]},"machine_name":{"type":["string","null"]}}},"AttachBuildCliWorkerResponse":{"type":"object","required":["session","worker","worker_token"],"properties":{"session":{"$ref":"#/components/schemas/Session"},"worker":{"$ref":"#/components/schemas/BuildCliWorkerStatus"},"worker_token":{"type":"string"}}},"AttachFolderResponse":{"type":"object","required":["folder_id"],"properties":{"folder_id":{"type":"string","description":"The created (hidden) storage folder now bound to the session."},"workspace_path":{"type":["string","null"],"description":"Where the folder is mounted in the VM. `None` when the session has no\nVM yet — the first bind mounts it and fills this in."}}},"BuildCliEventPage":{"type":"object","required":["data","has_more"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"]}}},"BuildCliMessageOrigin":{"type":"string","enum":["local","controller"]},"BuildCliSessionState":{"type":"object","required":["local_session_id"],"properties":{"local_session_id":{"type":"string","description":"UUID from the local JSONL session metadata. Stable across CLI resumes."},"worker_epoch":{"type":"integer","format":"int64","description":"Monotonic attachment generation. A new worker fences every older one.","minimum":0},"worker_metadata":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BuildCliWorkerMetadata","description":"Latest non-secret worker metadata, retained after disconnects and\nprocess restarts so history rows keep their machine and cwd context."}]}}},"BuildCliTextMessageRequest":{"type":"object","required":["id","text"],"properties":{"id":{"type":"string"},"text":{"type":"string"}}},"BuildCliWorkerEpochRequest":{"type":"object","required":["worker_epoch"],"properties":{"worker_epoch":{"type":"integer","format":"int64","minimum":0}}},"BuildCliWorkerEvent":{"oneOf":[{"type":"object","required":["id","text","type"],"properties":{"id":{"type":"string"},"text":{"type":"string"},"type":{"type":"string","enum":["local_user_message"]}}},{"type":"object","required":["id","text","type"],"properties":{"id":{"type":"string"},"text":{"type":"string"},"type":{"type":"string","enum":["agent_message"]}}}]},"BuildCliWorkerMetadata":{"type":"object","properties":{"cli_version":{"type":["string","null"]},"cwd":{"type":["string","null"]},"last_seen_at":{"type":["string","null"],"format":"date-time"},"machine_name":{"type":["string","null"]}}},"BuildCliWorkerResponse":{"type":"object","required":["worker"],"properties":{"worker":{"$ref":"#/components/schemas/BuildCliWorkerStatus"}}},"BuildCliWorkerStatus":{"allOf":[{"$ref":"#/components/schemas/BuildCliWorkerMetadata"},{"type":"object","required":["connected","ready","worker_epoch"],"properties":{"connected":{"type":"boolean"},"ready":{"type":"boolean"},"worker_epoch":{"type":"integer","format":"int64","minimum":0}}}],"description":"Live Build CLI worker projection used by both the worker endpoint and\nsession-list summaries. Capability tokens never enter this shape."},"ClonedRepo":{"type":"object","description":"Per-repo clone result reported by infra at VM creation (mirrors infra's\n`ClonedRepoResult`): `path` is set on success, `warning` on a non-fatal\nfailure (e.g. an auth/clone error for that one repo).","required":["url"],"properties":{"branch":{"type":["string","null"]},"checkout_sha":{"type":["string","null"]},"path":{"type":["string","null"]},"url":{"type":"string"},"warning":{"type":["string","null"]}}},"Cost":{"type":"object","description":"Dollar cost of a model call / loop / session, in **micro-USD** (1e-6 USD)\nintegers. Line items + their `total`; accumulate with [`Cost::accumulate`].","properties":{"cache_read":{"type":"integer","format":"int64","description":"Cache-read tokens × cache-read rate.","minimum":0},"cache_write":{"type":"integer","format":"int64","description":"Cache-write (Anthropic cache creation) tokens × cache-write rate. Always\n0 when the provider reports no cache-write token category.","minimum":0},"currency":{"type":"string","description":"Always `\"USD\"`; the values above are micro-USD."},"input":{"type":"integer","format":"int64","description":"Non-cached input tokens × input rate.","minimum":0},"output":{"type":"integer","format":"int64","description":"Output tokens × output rate.","minimum":0},"total":{"type":"integer","format":"int64","description":"Sum of the four line items.","minimum":0}}},"CreateAgentRequest":{"type":"object","description":"POST `/v1/agents` request body. Mirrors [`Agent`] minus the\nserver-assigned `id` / `version` / timestamps / `owner_sub` (stamped\nfrom the authenticated caller).","required":["name","model"],"properties":{"environment":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AgentEnvironment","description":"Ephemeral-VM spec (#173). When set, VM-backed runs of this agent\ndefault their template + repos from here. Validated at create\n(template allowlist + repo normalization)."}]},"mcp_servers":{"type":"array","items":{"$ref":"#/components/schemas/McpServerConfig"},"description":"Remote MCP servers (#142). Secret-free; validated at create."},"metadata":{"type":"object","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"model":{"type":"string"},"name":{"type":"string"},"org_id":{"type":["string","null"],"description":"Org to file the agent under (console grouping; `?org_id=` list\nfilter). Optional — NOT a security boundary, visibility stays\nowner-scoped regardless."},"reasoning_effort":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ReasoningEffort"}]},"system":{"type":["string","null"]},"tools":{"type":"array","items":{"$ref":"#/components/schemas/ToolRef"}}}},"CreateBuildCliSessionRequest":{"type":"object","required":["local_session_id"],"properties":{"local_session_id":{"type":"string"},"model":{"type":["string","null"]},"title":{"type":["string","null"]}}},"CreateCredentialRequest":{"type":"object","description":"POST `/v1/vaults/{id}/credentials` body.\n\nThe secret is **plaintext on the request only** — the handler encrypts it\nbefore it touches the store, and no response ever echoes it back.\n**Secret-bearing → extract with `agent_core::extract::ApiJsonRedacted`, never\nplain `ApiJson`**: a deser rejection on a secret field would otherwise echo\nits value into the warn log (#231), bypassing the redacting `Debug` below.","required":["display_name","auth"],"properties":{"auth":{"$ref":"#/components/schemas/CredentialAuthRequest"},"display_name":{"type":"string"}}},"CreateDeploymentRequest":{"type":"object","description":"POST `/v1/deployments` request body (#226).\n\n`agent` is the polymorphic [`AgentRef`] (bare id = latest at run time, or\n`{id, version}` to pin). `kickoff` is **required** (the seed `user.message`\neach run posts — like `Schedule.kickoff`); `environment` defaults to `None`\n(HTTP-only) and `vault_ids` to empty. No `trigger` field in v1 — deployments\nare Manual (automation linkage lands in P3).","required":["name","agent","kickoff"],"properties":{"agent":{"$ref":"#/components/schemas/AgentRef"},"environment":{"$ref":"#/components/schemas/DeploymentEnv"},"kickoff":{"type":"array","items":{"$ref":"#/components/schemas/TextBlock"}},"name":{"type":"string"},"vault_ids":{"type":"array","items":{"type":"string"}}},"additionalProperties":false},"CreateEnvironmentRequest":{"type":"object","description":"POST `/v1/environments` request body (Anthropic shape — see\n`docs/anthropic-managed-agents/CLOUD_ENV.md`).","required":["name","config"],"properties":{"config":{"$ref":"#/components/schemas/EnvironmentConfig"},"name":{"type":"string"}}},"CreateRunRequest":{"type":"object","description":"`POST /v1/runs` body. Exactly one selector: `mode` | `agent` | `builtin`.\n\n`Default` (all-`None`/empty) is for terse test construction via\n`..Default::default()` — every field is already `#[serde(default)]`, so it\nchanges no wire behavior.","properties":{"agent":{"type":["string","null"],"description":"A VM-backed agent the caller owns (`agt_…`), e.g. their `docs` agent.\nMutually exclusive with `mode` and `builtin`."},"builtin":{"type":["string","null"],"description":"A **public** built-in agent to run by slug (e.g. `ultraplan`) — runnable\nby ANY authed caller (it runs in the caller's own on-behalf-of VM).\nMutually exclusive with `mode` and `agent`. Driven by `request`, NOT\n`kickoff` (passing `kickoff` with `builtin` is a 400)."},"kickoff":{"type":["string","null"],"description":"Kickoff override — the first message the agent receives. For `agent`\nruns this is the natural way to describe the task (the client knows the\nPR/SHA/ref); absent, a sensible default is built per `mode`/`agent`.\nNot accepted for `builtin` runs (use `request`)."},"mode":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/FireMode","description":"Built-in reviewer to run (`review` → code-review, `ultrareview` →\nultra-review). Operator-only (needs the trigger machine identity).\nMutually exclusive with `agent` and `builtin`."}]},"pr_number":{"type":["integer","null"],"format":"int64","description":"PR number to review. Required for `mode` (the built-in reviewers post a\nPR review) unless an explicit `kickoff` is supplied. Ignored when\n`kickoff` is set."},"repos":{"type":"array","items":{"$ref":"#/components/schemas/GitRepo"},"description":"Repos to clone into the VM. Each repo's `path` defaults to\n`/workspace/<name>` for compatibility with review/run agents; bare\n`owner/name` URLs are expanded to `https://github.com/owner/name.git`.\nThe kickoff lists absolute clone paths, and VM file tools can use those\npaths directly. **Required (1+) for `mode`/`builtin`**; for `agent` it\nmay be empty — the run then gets a bare/workspace VM with no clone, and\nthe repos default from the agent's `environment` spec when omitted (#173)."},"request":{"type":["string","null"],"description":"The task prose for a `builtin` run (e.g. the planning request for\n`ultraplan`). **Required** for `builtin` (empty/missing → 400); the\nserver composes the kickoff as `clone_listing(repos) + \"\\n\\n\" + request`.\nIgnored for `mode`/`agent` (those use `kickoff`)."},"review":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ReviewRunRequest","description":"Exact diff + review target for public `builtin:\"ultrareview\"`. Rejected\n(400) on every other selector — `mode`/`agent`/non-`ultrareview` builtins\nuse `kickoff`/`request`."}]},"template_id":{"type":["string","null"],"description":"infra VM template (`workspace` / `development`). Optional; defaults from\nthe resolved `agent`'s `environment` spec when omitted, else infra's\ndefault. Validated against the allowlist (#173)."},"vault_ids":{"type":"array","items":{"type":"string"},"description":"Vaults to attach to the run (#142 M4 / #222). MCP credentials resolve\nper-turn; any `environment_variable` credentials are decrypted and\ninjected as VM env vars at provision (the agent process CAN read them,\nsame as `GH_TOKEN`). The caller must own each vault (404 otherwise)."}}},"CreateRunResponse":{"type":"object","description":"`POST /v1/runs` response.","required":["session_id","environment_id"],"properties":{"environment_id":{"type":"string","description":"The ephemeral environment provisioned for the run. Torn down\nserver-side when the run finishes (or at the run-timeout deadline)."},"session_id":{"type":"string","description":"The session running the agent. Stream it at\n`GET /v1/sessions/{id}/events/stream`; it's owned by the caller."}}},"CreateScheduleRequest":{"type":"object","description":"`POST /v1/schedules` body.","required":["agent","cron","timezone","kickoff"],"properties":{"agent":{"type":"string","description":"Agent id to run (`agt_...`)."},"cron":{"type":"string","description":"5-field cron expression, e.g. `0 9 * * 1-5`."},"kickoff":{"type":"array","items":{"$ref":"#/components/schemas/TextBlock"},"description":"Seed `user.message` content for each fired run."},"timezone":{"type":"string","description":"IANA timezone, e.g. `America/New_York`."}}},"CreateSessionRequest":{"type":"object","description":"POST `/v1/sessions` request body.\n\nThe agent reference is polymorphic — bare string id (latest version\nof the agent) or an object with both `id` and an explicit `version`\nto pin behavior across subsequent agent edits. Mirrors Anthropic's\nsession-create shape.","properties":{"agent":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AgentRef"}]},"environment":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/CreateWorkspaceEnvironmentRequest","description":"When a workspace session does not pass `environment_id`, the service\ncreates an environment from this optional config and binds it. If config\nis omitted, `build` defaults to the `development` template and `for_work`\ndefaults to the `workspace` template."}]},"environment_id":{"type":["string","null"],"description":"Bind the session to an environment (#67) for in-VM tool execution: the\n`shell` tool dispatches into the environment's VM (provisioned on first\nbinding, reused after). Optional — without it, exec tools have no target.\n\nThe caller MUST own the environment (a non-owned / unknown id → 404).\nThis replaces #68's interim caller-settable `vm_id`: `vm_id` is now\nserver-internal, derived from the bound environment, so a caller can no\nlonger point a session at an arbitrary VM (the cross-tenant fix)."},"folder_id":{"type":["string","null"]},"model":{"type":["string","null"]},"profile":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SessionProfile","description":"Product profile. `workspace` activates the shared Build/ForWork\nconvenience path and defaults `agent` to the built-in workspace agent."}]},"project_id":{"type":["string","null"]},"surface":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/WorkspaceSurface","description":"Workspace product grouping: `build` or `for_work`."}]},"title":{"type":["string","null"]},"vault_ids":{"type":"array","items":{"type":"string"},"description":"Vaults (#142 M4) to attach for MCP credential resolution. The caller\nMUST own every vault (a non-owned / unknown id → 404 at create). At MCP\nconnect time the first attached vault holding a credential whose\n`mcp_server_url` exactly matches the server wins."}}},"CreateTriggerRequest":{"type":"object","description":"`POST /v1/triggers` body.","required":["agent","repo","events"],"properties":{"agent":{"type":"string","description":"Agent id to run (`agt_...`). Must be owned by the caller."},"events":{"type":"array","items":{"type":"string"},"description":"Non-empty subset of [`ALLOWED_EVENTS`]."},"kickoff_template":{"type":["string","null"],"description":"Optional kickoff template (see [`Trigger::kickoff_template`])."},"repo":{"type":"string","description":"`owner/name` GitHub repo slug."}}},"CreateVaultRequest":{"type":"object","description":"POST `/v1/vaults` body. `owner` is the authenticated caller's `sub`,\nstamped server-side — never accepted from the client.","required":["display_name"],"properties":{"display_name":{"type":"string"},"metadata":{"type":"object","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}}}},"CreateWebhookRequest":{"type":"object","description":"`POST /v1/webhooks` body. The signing secret is NOT accepted from the\nclient — it is generated server-side and disclosed once in the response.","required":["url","events"],"properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"},"description":"At least one lifecycle event to subscribe to."},"url":{"type":"string","description":"Delivery target — SSRF-validated: https, or http to an\noperator-allowlisted internal host."}}},"CreateWorkspaceEnvironmentRequest":{"type":"object","description":"Optional environment seed for `POST /v1/sessions {profile:\"workspace\"}`.","properties":{"config":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/EnvironmentConfig"}]},"name":{"type":["string","null"]}}},"CredentialAuthRequest":{"oneOf":[{"type":"object","required":["mcp_server_url","token","type"],"properties":{"mcp_server_url":{"type":"string"},"token":{"type":"string","description":"The plaintext bearer. Write-only: never serialized in any response."},"type":{"type":"string","enum":["static_bearer"]}}},{"type":"object","description":"OAuth access token (#221). All secrets are plaintext on the request only;\nencrypted before storage, never echoed back.","required":["mcp_server_url","access_token","expires_at","type"],"properties":{"access_token":{"type":"string"},"expires_at":{"type":"string","format":"date-time"},"mcp_server_url":{"type":"string"},"refresh":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/OauthRefreshRequest"}]},"type":{"type":"string","enum":["mcp_oauth"]}}},{"type":"object","description":"A plaintext secret injected as a VM environment variable at provision time\n(#222). `value` is plaintext on the request only — encrypted before\nstorage, never echoed back.","required":["secret_name","value","type"],"properties":{"secret_name":{"type":"string"},"type":{"type":"string","enum":["environment_variable"]},"value":{"type":"string"}}},{"type":"object","description":"Custom HTTP headers for a header-auth MCP server (#215). Header NAME →\nplaintext VALUE; each value is encrypted before storage and never echoed\nback (the response carries only the names). Keys on `mcp_server_url`.","required":["mcp_server_url","headers","type"],"properties":{"headers":{"type":"object","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"mcp_server_url":{"type":"string"},"type":{"type":"string","enum":["static_headers"]}}}],"description":"Inbound credential auth (plaintext).\n\nTagged `type` like the domain enum: `static_bearer`/`mcp_oauth` (MCP\ncredentials, keyed on `mcp_server_url`) + `environment_variable` (#222, keyed\non `secret_name`). `Debug` is hand-written to REDACT every plaintext secret\n(write-only — they must never reach a log/trace)."},"CredentialValidation":{"type":"object","description":"The `mcp_oauth_validate` diagnose report (#221).\n\n`POST /v1/vaults/{id}/credentials/{cid}/mcp_oauth_validate` returns this. The\nheadline `status` tells the caller what to do; `refresh` + `mcp_probe` carry\nthe detail. No secret ever appears here (write-only), so it derives `Debug`.","required":["type","credential_id","vault_id","validated_at","has_refresh_token","status","refresh","mcp_probe"],"properties":{"credential_id":{"type":"string"},"has_refresh_token":{"type":"boolean"},"mcp_probe":{"$ref":"#/components/schemas/McpProbeReport"},"refresh":{"$ref":"#/components/schemas/RefreshReport"},"status":{"$ref":"#/components/schemas/ValidationStatus"},"type":{"type":"string","description":"Always `\"vault_credential_validation\"`."},"validated_at":{"type":"string","format":"date-time"},"vault_id":{"type":"string"}}},"Deployment":{"type":"object","description":"A named, reusable run-config. Also the create/get/list response body.","required":["id","owner_sub","name","agent_id","environment","kickoff","status","created_at","updated_at"],"properties":{"agent_id":{"type":"string","description":"Agent to run (`agt_…`)."},"agent_version":{"type":["integer","null"],"format":"int32","description":"Pinned agent version — `None` resolves the latest version at run time\n(matches `AgentRef` semantics).","minimum":0},"created_at":{"type":"string","format":"date-time"},"environment":{"$ref":"#/components/schemas/DeploymentEnv","description":"Where the run executes: HTTP-only, a reused pre-made VM, or a fresh\nephemeral VM per run."},"id":{"type":"string","description":"`depl_<uuid>`. Immutable."},"kickoff":{"type":"array","items":{"$ref":"#/components/schemas/TextBlock"},"description":"Seed `user.message` content posted when a run fires (same shape as\n`Schedule.kickoff`)."},"name":{"type":"string"},"owner_sub":{"type":"string","description":"JWT `sub` of the owning user. Every CRUD op is scoped to it (like\n`Schedule`/`Trigger`)."},"status":{"$ref":"#/components/schemas/DeploymentStatus"},"updated_at":{"type":"string","format":"date-time"},"vault_ids":{"type":"array","items":{"type":"string"},"description":"Vaults attached to each run (#142 M4 / #222) for MCP credential\nresolution; on the `Ephemeral` lane, any `environment_variable`\ncredentials are also injected as VM env vars at provision."}}},"DeploymentEnv":{"oneOf":[{"type":"object","description":"HTTP-only — no VM. The run is a plain agent turn (like a scheduled\nHTTP-only fire).","required":["type"],"properties":{"type":{"type":"string","enum":["none"]}}},{"type":"object","description":"Bind a pre-made environment the caller owns; its VM is provisioned on\nfirst bind and **reused** across runs.","required":["environment_id","type"],"properties":{"environment_id":{"type":"string"},"type":{"type":"string","enum":["existing"]}}},{"type":"object","description":"Provision a fresh VM per run (cloning `repos`, torn down when the run\nfinishes) — the `RunService::launch` path. `template_id` defaults to\ninfra's choice and may be `workspace`, `development`, or `browser`;\n`repos` may be empty (a bare/workspace VM).","required":["type"],"properties":{"repos":{"type":"array","items":{"$ref":"#/components/schemas/GitRepo"}},"template_id":{"type":["string","null"]},"type":{"type":"string","enum":["ephemeral"]}}}],"description":"A deployment's environment: a discriminated choice so **build agents work**.\n\n`None` (HTTP-only) | `Existing` (bind a pre-made, **reused** VM —\nworkspace/standing agents) | `Ephemeral` (a **fresh** VM provisioned per run\nand torn down — build/PR agents need this). The two non-`None` arms map to\nthe two existing services: `EnvironmentService::bind_as` (reuse) and\n`RunService::launch` (fresh-provision + clone + teardown). Tagged `type` like\n[`super::session::SessionStatus`]."},"DeploymentList":{"type":"object","description":"`GET /v1/deployments` response — projects each [`Deployment`] directly (the\ndomain type doubles as its own response body, like `Schedule`).","required":["data","has_more"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Deployment"}},"has_more":{"type":"boolean","description":"Always `false` in v1 — the endpoint returns every deployment the caller\nowns. Reserved for cursor pagination."}}},"DeploymentStatus":{"type":"string","description":"Lifecycle state. `Paused` is reserved for P3 (unattended firing) — a manual\n`/run` ignores it in v1; it exists so the console can render the toggle.","enum":["active","paused"]},"EnvironmentConfig":{"type":"object","description":"Anthropic-shaped environment config (`docs/anthropic-managed-agents/CLOUD_ENV.md`).","required":["type"],"properties":{"folder_id":{"type":["string","null"],"description":"storage-api folder to mount into the VM at first bind, forwarded to\ninfra's `CreateVmRequest.folder_id` (drives the workspace mount +\nworkspace-sync). Caller-provided only — `None` means the VM gets no\nfolder; neither this service nor infra ever auto-creates one. Set from\nthe session's `folder_id` for workspace sessions; a warm-reused\nenvironment keeps its first-bind mount (no retro-mount)."},"git_repos":{"type":"array","items":{"$ref":"#/components/schemas/GitRepo"},"description":"Repos to clone into the VM at creation (#93), forwarded to infra. They\nclone as the VM's **owner**, so private repos pull with that identity's\nGitHub creds. infra clones each to `/home/user/<name>` (or `path`) and\nreports the result, surfaced as the session's `cloned_repos`."},"networking":{"$ref":"#/components/schemas/Networking"},"packages":{"$ref":"#/components/schemas/Packages","description":"Packages to install per manager. v1 stores these but does NOT install\nthem yet (provisions a bare dev VM) — install orchestration is a tracked\nfollow-up."},"template_id":{"type":["string","null"],"description":"infra VM template to provision. `None` → infra's default (`development`).\nSet to `workspace` for the template that bakes the skills repo into\n`/mnt/skills`, or `browser` for Chrome + agent-browser. Forwarded\nverbatim to infra's `CreateVmRequest.template_id` at first bind."},"type":{"$ref":"#/components/schemas/EnvironmentType","description":"Only `cloud` is supported in v1; `self_hosted` is reserved."}}},"EnvironmentList":{"type":"object","description":"`GET /v1/environments` response — projects each [`Environment`] through\n[`EnvironmentResponse`] so `owner`/`vm_id` never reach the wire.","required":["data","has_more"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EnvironmentResponse"}},"has_more":{"type":"boolean","description":"Always `false` in v1 — the list returns every owned environment."}}},"EnvironmentResponse":{"type":"object","description":"Anthropic-shaped environment response — projects [`Environment`], omitting\nthe internal `owner` / `vm_id` (the `id` is the only handle a caller gets).","required":["id","name","config","state","created_at"],"properties":{"archived_at":{"type":["string","null"],"format":"date-time"},"config":{"$ref":"#/components/schemas/EnvironmentConfig"},"created_at":{"type":"string","format":"date-time"},"id":{"type":"string"},"name":{"type":"string"},"state":{"$ref":"#/components/schemas/EnvironmentState"}}},"EnvironmentState":{"type":"string","description":"Lifecycle state. `Archived` is a soft-delete: the row + config persist, but\nnew sessions can't bind it and the backing VM is torn down.","enum":["active","archived"]},"EnvironmentType":{"type":"string","enum":["cloud"]},"ErrorBody":{"type":"object","required":["error"],"properties":{"error":{"$ref":"#/components/schemas/ErrorDetail"}}},"ErrorDetail":{"type":"object","required":["message","type"],"properties":{"message":{"type":"string","example":"model not supported: foo"},"type":{"type":"string","example":"invalid_request_error"}}},"Event":{"oneOf":[{"type":"object","description":"User text executed by an attached local Build CLI. The origin is\ndurable so worker command replay can select controller-authored turns\nwithout feeding locally-authored turns back to the same worker.","required":["id","content","origin","type"],"properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/TextBlock"}},"id":{"type":"string"},"origin":{"$ref":"#/components/schemas/BuildCliMessageOrigin"},"processed_at":{"type":["string","null"],"format":"date-time"},"type":{"type":"string","enum":["build_cli.user_message"]}}},{"type":"object","description":"Client-driven turn input.","required":["id","content","type"],"properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/TextBlock"}},"id":{"type":"string"},"mcp_services":{"type":"array","items":{"type":"string"},"description":"Curated hosted MCP service ids selected for this message only."},"processed_at":{"type":["string","null"],"format":"date-time"},"type":{"type":"string","enum":["user.message"]}}},{"type":"object","description":"Stop the agent mid-turn. The next `user.message` resumes from the\npost-interrupt state.","required":["id","type"],"properties":{"id":{"type":"string"},"processed_at":{"type":["string","null"],"format":"date-time"},"type":{"type":"string","enum":["user.interrupt"]}}},{"type":"object","description":"Reasoning block emitted on block-close. Carries the full replay\npayload — `reasoning_id` (provider id; required by `OpenAI` Responses\nAPI on replay) and `content` parts. Anthropic's per-block\nsignature lives inside `ReasoningPart::Text { signature }`; there\nis no top-level signature on the event itself (the upstream spec\ndoesn't define one). Persisted verbatim so the next turn can\nreplay unchanged.","required":["id","content","type"],"properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/ReasoningPart"}},"id":{"type":"string"},"processed_at":{"type":["string","null"],"format":"date-time"},"reasoning_id":{"type":["string","null"]},"type":{"type":"string","enum":["agent.thinking"]}}},{"type":"object","description":"Assistant text emitted by the model.","required":["id","content","type"],"properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/TextBlock"}},"id":{"type":"string"},"processed_at":{"type":["string","null"],"format":"date-time"},"type":{"type":"string","enum":["agent.message"]}}},{"type":"object","description":"Live-only assistant text delta. Broadcast to streaming clients for\ntyping UX; durable history still uses coalesced `agent.message` events.","required":["id","delta","type"],"properties":{"delta":{"type":"string"},"id":{"type":"string"},"processed_at":{"type":["string","null"],"format":"date-time"},"type":{"type":"string","enum":["agent.message_delta"]}}},{"type":"object","description":"Model invoked a tool. The event's own `id` is the internal\ncorrelator (`evt_<uuid>`) that the corresponding\n`agent.tool_result` references.\n\n`provider_id` + `provider_call_id` preserve the upstream\ntool-call identifiers verbatim for replay. `OpenAI`'s Responses\nAPI rejects `function_call` items whose id wasn't one it\nissued (`fc_*` / `call_*`); `Anthropic` tolerates arbitrary\nids but only emits one. Both fields default to `None` for serde\nback-compat with pre-field events — but `Event::agent_tool_use`\nrequires `provider_id` at construction time, so any code path\nthat mints a fresh event must carry it through.","required":["id","tool","input","type"],"properties":{"id":{"type":"string"},"input":{},"processed_at":{"type":["string","null"],"format":"date-time"},"provider_call_id":{"type":["string","null"]},"provider_id":{"type":["string","null"]},"tool":{"type":"string"},"type":{"type":"string","enum":["agent.tool_use"]}}},{"type":"object","description":"Runtime-computed tool output. `tool_use_id` is the internal\n`evt_*` correlator from the originating `agent.tool_use`;\n`is_error: true` signals the tool itself failed (the LLM sees\nthis and can recover — session stays alive). `provider_call_id`\nis the upstream call-correlation id (`OpenAI` requires it on\n`function_call_output`; `Anthropic` ignores it). Defaults to\n`None` on pre-field events; replay falls back to `tool_use_id`.","required":["id","tool_use_id","content","type"],"properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/ToolResultBlock"}},"id":{"type":"string"},"is_error":{"type":"boolean"},"output":{},"processed_at":{"type":["string","null"],"format":"date-time"},"provider_call_id":{"type":["string","null"]},"tool_use_id":{"type":"string"},"type":{"type":"string","enum":["agent.tool_result"]}}},{"type":"object","description":"Live-only workspace VM lifecycle signal. Broadcast while binding,\nprovisioning, resuming, or failing before a model step starts.","required":["id","status","type"],"properties":{"environment_id":{"type":["string","null"]},"id":{"type":"string"},"message":{"type":["string","null"]},"processed_at":{"type":["string","null"],"format":"date-time"},"status":{"$ref":"#/components/schemas/WorkspaceLifecycleStatus"},"type":{"type":"string","enum":["workspace.lifecycle"]},"vm_id":{"type":["string","null"]}}},{"type":"object","description":"Agent has started processing a turn.","required":["id","type"],"properties":{"id":{"type":"string"},"processed_at":{"type":["string","null"],"format":"date-time"},"type":{"type":"string","enum":["session.status_running"]}}},{"type":"object","description":"Agent finished a turn and is awaiting input. Carries THIS loop's\n(per-turn) token usage and dollar cost (#157) — the sum of the turn's\nmodel calls — so a client reads the per-loop total directly off the\nevent without summing `span.model_request_end` deltas itself.","required":["id","type"],"properties":{"cost":{"$ref":"#/components/schemas/Cost","description":"This loop's dollar cost, priced at run time (micro-USD). (#157)"},"id":{"type":"string"},"processed_at":{"type":["string","null"],"format":"date-time"},"stop_reason":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/StopReason"}]},"type":{"type":"string","enum":["session.status_idle"]},"usage":{"$ref":"#/components/schemas/Usage","description":"This loop's token usage (sum of the turn's model calls). (#157)"}}},{"type":"object","description":"Unrecoverable session error. The session moves to `terminated`.","required":["id","error","type"],"properties":{"error":{"$ref":"#/components/schemas/EventError"},"id":{"type":"string"},"processed_at":{"type":["string","null"],"format":"date-time"},"type":{"type":"string","enum":["session.status_terminated"]}}},{"type":"object","description":"Recoverable error mid-turn — the session stays alive, the client can\nretry. Typed `error` so clients can distinguish causes\nprogrammatically.","required":["id","error","type"],"properties":{"error":{"$ref":"#/components/schemas/EventError"},"id":{"type":"string"},"processed_at":{"type":["string","null"],"format":"date-time"},"type":{"type":"string","enum":["session.error"]}}},{"type":"object","description":"Observability marker: a model inference call started.","required":["id","model","type"],"properties":{"id":{"type":"string"},"model":{"type":"string"},"processed_at":{"type":["string","null"],"format":"date-time"},"type":{"type":"string","enum":["span.model_request_start"]}}},{"type":"object","description":"Observability marker: model inference completed; `usage` carries the\nper-call token deltas (accumulated into `session.usage`).","required":["id","model","usage","type"],"properties":{"cost":{"$ref":"#/components/schemas/Cost","description":"Frozen per-call cost at the rates in effect when the call ran."},"id":{"type":"string"},"model":{"type":"string"},"processed_at":{"type":["string","null"],"format":"date-time"},"type":{"type":"string","enum":["span.model_request_end"]},"usage":{"$ref":"#/components/schemas/Usage"}}}],"description":"Every observable thing that happens inside a session.\n\nPersisted to `events.jsonl` per session, broadcast on the per-session\nbus for SSE subscribers, and returned by `GET /v1/sessions/:id/events`.\nThe serde shape uses an internal `type` discriminator matching the\n[Managed Agents event-stream\nspec](../../../../docs/anthropic-managed-agents/SESSION_EVENT_STREAM.md):\n`{\"type\": \"user.message\", \"id\": \"evt_...\", \"content\": [...], ...}`.\n\nEvery variant carries:\n- `id` — `evt_<uuid>`, immutable.\n- `processed_at` — server-side processed timestamp. `None` while queued\n  behind preceding events; set when the runtime begins handling the\n  event (matches the spec semantics)."},"EventError":{"type":"object","description":"Typed error carried on `session.error` and `session.status_terminated`.\n\n`type` is a short identifier (`upstream_error`, `tool_error`, …) the\nclient matches on; `message` is the human-readable detail.","required":["type","message"],"properties":{"message":{"type":"string"},"type":{"type":"string"}}},"EventList":{"type":"object","description":"`GET /v1/sessions/:id/events` response. Also `Deserialize` so\nin-workspace clients (e.g. `managed-agents-evals`) can decode this\nshape without re-declaring it.","required":["data","has_more"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"has_more":{"type":"boolean","description":"Always `false` in v1 — the endpoint returns the entire event log\nfor a session. Sessions with very long histories may grow this\ninto a real cursor scheme later."}}},"FireLane":{"type":"string","description":"Which path inserted a fire row.","enum":["mention","auto"]},"FireList":{"type":"object","description":"`GET /v1/triggers/{id}/fires` response.","required":["data","has_more"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TriggerFire"}},"has_more":{"type":"boolean","description":"Always `false` in v1 — reserved for cursor pagination."}}},"FireMode":{"type":"string","description":"Mention-lane agent selection (`@cluster-build review` vs `ultrareview`).","enum":["review","ultrareview"]},"FireStatus":{"type":"string","description":"Fire lifecycle: `pending → running → completed | error | timeout`.\n\nPlus `pending → error` (fail-before-session). `timeout` means the watcher\ngave up waiting — the run itself is never aborted and the env is left up.","enum":["pending","running","completed","error","timeout"]},"GitRepo":{"type":"object","description":"One repo to clone at VM creation. Mirrors infra's `GitRepoSpec`.","required":["url"],"properties":{"branch":{"type":["string","null"]},"checkout_sha":{"type":["string","null"],"description":"Optional commit SHA to checkout after cloning the branch/ref."},"path":{"type":["string","null"],"description":"Destination dir; empty → infra clones to `/home/user/<repo_name>`."},"url":{"type":"string"}}},"HookAck":{"type":"object","description":"`POST /v1/hooks/github` response.","required":["accepted","duplicates"],"properties":{"accepted":{"type":"integer","description":"Fires inserted + spawned by THIS delivery.","minimum":0},"duplicates":{"type":"integer","description":"Fires this delivery matched that already existed (replayed\ndelivery GUID) — at-most-once in action.","minimum":0}}},"HookHead":{"type":"object","description":"The PR ref to clone. For opened/ready events, `clone_url` points at the\nPR head repo (the fork, when `is_fork`).\n\nFor merged events, infra carries the base repo/ref plus the merge commit\nSHA so source checkout survives deleted PR branches.","required":["ref","sha","clone_url","is_fork"],"properties":{"clone_url":{"type":"string"},"is_fork":{"type":"boolean"},"ref":{"type":"string"},"sha":{"type":"string"}}},"HookMention":{"type":"object","description":"Mention-lane payload: who asked for what.","required":["mode","instruction","login"],"properties":{"instruction":{"type":"string","description":"Free text after the trigger phrase, trimmed; may be empty."},"login":{"type":"string","description":"GitHub login of the commenter."},"mode":{"$ref":"#/components/schemas/FireMode"}}},"McpProbeReport":{"type":"object","description":"The MCP `initialize` probe leg: `reachable` = the handshake succeeded with the\n(refreshed) access token; `error` is the coarse connect error otherwise.","required":["method","reachable"],"properties":{"error":{"type":["string","null"]},"method":{"type":"string"},"reachable":{"type":"boolean"}}},"McpServerConfig":{"type":"object","description":"One remote MCP server an agent connects to. Streamable HTTP only in v1.\n\n`deny_unknown_fields`: a typo'd or camelCase key (`allowedTools`, `auht`)\nwould otherwise be silently dropped by serde — and since `allowed_tools:\nNone` means *allow every tool* and absent `auth` means *unauthenticated*,\na silent drop **widens a security policy**. Reject the unknown key instead.","required":["name","url"],"properties":{"allowed_tools":{"type":["array","null"],"items":{"type":"string"},"description":"Enable-list of raw (un-namespaced) tool names the agent may use from\nthis server. `None` = every tool the server advertises. Validated\nnon-empty when present (an empty list would disable the server, which\nis a config mistake — drop the server instead)."},"auth":{"$ref":"#/components/schemas/AgentMcpAuth","description":"How to authenticate. Secret-free: `none` or `token_source`. Defaults\nto `none`; omitted from responses when unauthenticated."},"name":{"type":"string","description":"Unique-within-the-agent server name; the `mcp__<name>__<tool>`\nnamespacing segment. Validated via `agent_mcp::validate_server_name`."},"tool_timeout_secs":{"type":["integer","null"],"format":"int64","description":"Per-`tools/call` timeout. `None` = the crate default (120s). Validated\n`1..=600`.","maximum":600,"minimum":1},"url":{"type":"string","description":"Streamable-HTTP endpoint. Validated by the SSRF guard\n(`https` + public host, or an operator-allow-listed internal host)."}},"additionalProperties":false},"McpServerTools":{"type":"object","description":"One server's discovered tools (or its connect error).","required":["server","tools"],"properties":{"error":{"type":["string","null"],"description":"Present when the server failed to connect; `tools` is then empty."},"server":{"type":"string"},"tools":{"type":"array","items":{"$ref":"#/components/schemas/McpToolInfo"}}}},"McpServiceAuthMode":{"type":"string","description":"Authentication mode advertised by a curated hosted MCP service.","enum":["oauth"]},"McpServiceBackend":{"type":"string","description":"Runtime implementation behind a curated connector.","enum":["hosted_mcp","native_gmail"]},"McpServiceConnectCompleteRequest":{"type":"object","description":"Complete a hosted MCP service OAuth authorization-code flow.","required":["state"],"properties":{"code":{"type":["string","null"]},"error":{"type":["string","null"]},"error_description":{"type":["string","null"]},"state":{"type":"string"}}},"McpServiceConnectCompleteResponse":{"type":"object","description":"Result of completing a hosted MCP service OAuth flow.","required":["service_id","return_url","connected"],"properties":{"connected":{"type":"boolean"},"error":{"type":["string","null"]},"return_url":{"type":"string"},"service_id":{"type":"string"}}},"McpServiceConnectStartRequest":{"type":"object","description":"Start a hosted MCP service OAuth authorization-code flow.","required":["return_url"],"properties":{"return_url":{"type":"string","description":"Absolute console URL or configured native-app callback URI for the OAuth\nprovider to return to."}}},"McpServiceConnectStartResponse":{"type":"object","description":"Provider authorization URL for the browser to visit.","required":["authorization_url","expires_at"],"properties":{"authorization_url":{"type":"string"},"expires_at":{"type":"string","format":"date-time"}}},"McpServiceList":{"type":"object","description":"`GET /v1/mcp-services` response.","required":["data","has_more"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/McpServiceResponse"}},"has_more":{"type":"boolean"}}},"McpServiceResponse":{"type":"object","description":"One curated hosted MCP service exposed to clients.","required":["id","display_name","description","server_name","mcp_server_url","auth_mode","backend","connected"],"properties":{"auth_mode":{"$ref":"#/components/schemas/McpServiceAuthMode"},"backend":{"$ref":"#/components/schemas/McpServiceBackend"},"connected":{"type":"boolean"},"description":{"type":"string"},"display_name":{"type":"string"},"id":{"type":"string"},"mcp_server_url":{"type":"string"},"server_name":{"type":"string"}}},"McpServiceToolList":{"type":"object","description":"`GET /v1/mcp-services/{id}/tools` response.","required":["service_id","disabled_tools","data","has_more"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/McpServiceToolResponse"}},"disabled_tools":{"type":"array","items":{"type":"string"},"description":"Authoritative user-disabled raw tool names for this service. This may\ninclude tools the MCP server is not advertising in the current live\ncatalog, so clients must preserve it when submitting replacement writes."},"has_more":{"type":"boolean"},"service_id":{"type":"string"}}},"McpServiceToolResponse":{"type":"object","description":"One tool advertised by a curated hosted MCP service.","required":["name","description","schema","enabled"],"properties":{"description":{"type":"string"},"disabled_reason":{"type":["string","null"]},"enabled":{"type":"boolean","description":"False when the user disabled this tool or the provider-safe namespaced\nname cannot be exposed to the model."},"name":{"type":"string","description":"Raw MCP tool name, before the model-facing `mcp__server__tool` prefix."},"schema":{}}},"McpServiceToolSelectionRequest":{"type":"object","description":"Replace the current user-disabled tool list for a hosted MCP service.","required":["disabled_tools"],"properties":{"disabled_tools":{"type":"array","items":{"type":"string"}}},"additionalProperties":false},"McpServiceToolSelectionResponse":{"type":"object","description":"Saved user-disabled tool list for a hosted MCP service.","required":["service_id","disabled_tools"],"properties":{"disabled_tools":{"type":"array","items":{"type":"string"}},"service_id":{"type":"string"}}},"McpToolInfo":{"type":"object","description":"One discovered tool: raw (un-namespaced) name + normalized schema.","required":["name","description","schema"],"properties":{"description":{"type":"string"},"name":{"type":"string"},"schema":{}}},"McpToolsResponse":{"type":"object","description":"`GET /v1/agents/{id}/mcp-tools` response — live discovery of each declared\nserver's tools.\n\nFor the console `allowed_tools` picker (#142 M2, docs#20 Phase B). The\ncatalog is **unfiltered** (every tool the server advertises) so the UI can\noffer all of them; a server that failed to connect carries its `error`.","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/McpServerTools"}}}},"ModelInfo":{"type":"object","required":["id","object","created","owned_by","deprecated","context_window","default_max_tokens","supports_attachments","can_reason","pricing"],"properties":{"can_reason":{"type":"boolean"},"context_window":{"type":"integer","format":"int64","example":200000,"minimum":0},"created":{"type":"integer","format":"int64"},"default_max_tokens":{"type":"integer","format":"int64","example":64000,"minimum":0},"default_reasoning_effort":{"type":["string","null"]},"deprecated":{"type":"boolean","description":"Supported but superseded model. Clients may group these under a legacy\nor \"more models\" affordance; requests remain valid."},"fixed_reasoning_effort":{"type":["string","null"],"description":"Product aliases may pin effort instead of exposing a reasoning control."},"id":{"type":"string","example":"gpt-5.6"},"object":{"type":"string","example":"model"},"owned_by":{"type":"string","example":"openai"},"pricing":{"$ref":"#/components/schemas/Pricing"},"reasoning_levels":{"type":"array","items":{"type":"string"}},"supports_attachments":{"type":"boolean"}}},"ModelsResponse":{"type":"object","required":["object","data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ModelInfo"}},"object":{"type":"string","example":"list"}}},"Networking":{"type":"object","description":"Network policy. v1 supports only `unrestricted`; `limited` is rejected with\na 400 at create until cluster/infra ships network filtering.","required":["type"],"properties":{"allowed_hosts":{"type":"array","items":{"type":"string"}},"type":{"$ref":"#/components/schemas/NetworkingType"}}},"NetworkingType":{"type":"string","enum":["unrestricted","limited"]},"NormalizedHookEvent":{"type":"object","description":"The cross-repo wire contract: one normalized GitHub event.\n\nProduced by infra-api's webhook receiver (Go), consumed by\n`POST /v1/hooks/github`. Field names are normative; both sides pin them\nagainst byte-identical fixtures. Deliberately NOT `deny_unknown_fields` —\nthe contract evolves additively (`pull_request.merged` is a named\nfollow-up).","required":["delivery_id","event","repo","pr_number","head"],"properties":{"delivery_id":{"type":"string","description":"`X-GitHub-Delivery` GUID, verbatim."},"event":{"type":"string","description":"`issue_comment.created` | `pull_request.opened` |\n`pull_request.ready_for_review` | `pull_request.merged`. A plain\nstring by design."},"head":{"$ref":"#/components/schemas/HookHead","description":"Always present and already resolved — the receiver does the PR fetch\nfor `issue_comment` deliveries."},"mention":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/HookMention","description":"`None` ⇒ auto lane."}]},"pr_number":{"type":"integer","format":"int64"},"repo":{"type":"string","description":"`owner/name` of the repo the event fired on."}}},"OauthRefreshRequest":{"type":"object","description":"The refresh half of an `mcp_oauth` create request (#221). `refresh_token` (and\nany `client_secret`) are plaintext on the request only — write-only.","required":["token_endpoint","client_id","refresh_token","token_endpoint_auth"],"properties":{"client_id":{"type":"string"},"refresh_token":{"type":"string"},"scope":{"type":["string","null"]},"token_endpoint":{"type":"string"},"token_endpoint_auth":{"$ref":"#/components/schemas/TokenEndpointAuthRequest"}}},"Packages":{"type":"object","description":"Per-manager package lists, in Anthropic's stated install order.","properties":{"apt":{"type":"array","items":{"type":"string"}},"cargo":{"type":"array","items":{"type":"string"}},"gem":{"type":"array","items":{"type":"string"}},"go":{"type":"array","items":{"type":"string"}},"npm":{"type":"array","items":{"type":"string"}},"pip":{"type":"array","items":{"type":"string"}}}},"PatchDeploymentRequest":{"type":"object","description":"PATCH `/v1/deployments/{id}` body (#226). Every field optional; absent means\nunchanged. The agent reference is immutable post-create (not patchable here).","properties":{"environment":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/DeploymentEnv"}]},"kickoff":{"type":["array","null"],"items":{"$ref":"#/components/schemas/TextBlock"}},"name":{"type":["string","null"]},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/DeploymentStatus"}]},"vault_ids":{"type":["array","null"],"items":{"type":"string"}}},"additionalProperties":false},"PatchSessionRequest":{"type":"object","description":"PATCH `/v1/sessions/{id}` body for lightweight session metadata/lifecycle.","properties":{"archived":{"type":["boolean","null"]},"title":{"type":["string","null"]}},"additionalProperties":false},"Pricing":{"type":"object","required":["input_per_mtok","output_per_mtok","input_includes_cache_read","currency"],"properties":{"cache_read_per_mtok":{"type":["number","null"],"format":"double","description":"Cache-read rate. Omitted when the model has no cache pricing.","example":0.3},"cache_write_per_mtok":{"type":["number","null"],"format":"double","description":"Cache-write rate. Omitted when the model has no distinct cache-write charge.","example":3.75},"currency":{"type":"string","example":"USD"},"input_includes_cache_read":{"type":"boolean","description":"Whether reported input tokens already include cache-read tokens."},"input_per_mtok":{"type":"number","format":"double","example":3.0},"long_context_input_multiplier":{"type":["number","null"],"format":"double"},"long_context_output_multiplier":{"type":["number","null"],"format":"double"},"long_context_threshold_tokens":{"type":["integer","null"],"format":"int64","description":"Input-token threshold above which the long-context multipliers apply\nto the entire request.","minimum":0},"output_per_mtok":{"type":"number","format":"double","example":15.0}}},"ReasoningEffort":{"type":"string","description":"Per-turn reasoning depth selector. Catwalk-validated per model.\n\nWire string forms (the serde `rename_all = \"lowercase\"` form): `none`,\n`minimal`, `low`, `medium`, `high`, `xhigh`, `max`. Provider-specific\nallowed lists are enforced by [`validate_effort_for_model`].","enum":["none","minimal","low","medium","high","xhigh","max"]},"ReasoningPart":{"oneOf":[{"type":"object","required":["text","type"],"properties":{"signature":{"type":["string","null"]},"text":{"type":"string"},"type":{"type":"string","enum":["text"]}}},{"type":"object","required":["data","type"],"properties":{"data":{"type":"string"},"type":{"type":"string","enum":["encrypted"]}}},{"type":"object","required":["text","type"],"properties":{"text":{"type":"string"},"type":{"type":"string","enum":["summary"]}}},{"type":"object","required":["data","type"],"properties":{"data":{"type":"string"},"type":{"type":"string","enum":["redacted"]}}}],"description":"One element of a replayed reasoning block.\n\nMirrors what providers emit:\n\n- `text` — a thinking-text block (Anthropic carries an integrity\n  `signature`; `OpenAI` summary parts use `summary` instead).\n- `summary` — `OpenAI` Responses-API summary text.\n- `encrypted` — opaque blob `OpenAI` emits under\n  `reasoning.encrypted_content` so clients can replay reasoning items\n  when the upstream is stateless (`store: false`).\n- `redacted` — opaque data preserved when Anthropic filters thinking\n  content; replay verbatim to keep the block sequence intact.\n\nOn replay, send every part the gateway emitted for a given reasoning\nitem back unmodified — providers reject tampered signatures and 404\nreasoning ids whose content is missing under `store: false`."},"RefreshReport":{"type":"object","description":"The refresh leg of the diagnosis: `status` ∈ {`refreshed`, `fresh`,\n`no_refresh_token`, `invalid`, `unknown`}; `status_code`/`error` carry the\ntoken-endpoint HTTP failure detail when present.","required":["status"],"properties":{"error":{"type":["string","null"]},"status":{"type":"string"},"status_code":{"type":["integer","null"],"format":"int32","minimum":0}}},"ReviewDelivery":{"oneOf":[{"type":"object","required":["repo","type"],"properties":{"branch":{"type":["string","null"]},"pr_number":{"type":["integer","null"],"format":"int64"},"repo":{"type":"string"},"type":{"type":"string","enum":["pr_comment"]}}}],"description":"Optional delivery target — where to post the single consolidated review."},"ReviewRunRequest":{"type":"object","description":"`review` payload for `builtin:\"ultrareview\"`: what the review targets, the\nchange under review (exactly one of `source` or `diff`), and where (if\nanywhere) to post the verdict.","required":["target"],"properties":{"delivery":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ReviewDelivery"}]},"diff":{"type":["string","null"],"description":"Legacy inline diff, base64-staged through the model and capped at\n[`MAX_REVIEW_DIFF_BYTES`]. Mutually exclusive with `source`."},"source":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ReviewSource","description":"VM-derives the diff itself from a pushed target (#176). Mutually\nexclusive with `diff` — exactly one must be set. The proper path: no\nbase64, no size cap."}]},"target":{"$ref":"#/components/schemas/ReviewTarget"}}},"ReviewSource":{"oneOf":[{"type":"object","description":"`git diff origin/<base>...origin/<branch>` — needs no `gh`: the\non-behalf-of clone is a FULL clone (infra sends `Depth: 0` with\n`--branch` but never `--single-branch`), so every `origin/*` ref is\npresent and a pushed base/branch diff needs no fetch. The\nbuild-cli-driven path.","required":["base","branch","kind"],"properties":{"base":{"type":"string"},"branch":{"type":"string"},"kind":{"type":"string","enum":["branch"]}}},{"type":"object","description":"`gh pr diff <number>` — relies on in-VM `gh` auth (present on the\nmachine/mention identity; on the on-behalf-of lane it depends on infra\ninjecting the caller's GitHub creds, which the orchestrator checks and\nSTOPs on if absent).","required":["number","kind"],"properties":{"kind":{"type":"string","enum":["pr"]},"number":{"type":"integer","format":"int64"}}}],"description":"How the VM derives `/workspace/pr.diff` for a pushed target (#176). The diff\nis produced **in-VM by a fixed scripted command** — never base64'd through\nthe model. Both refs must be pushed (Case A); local/uncommitted state has no\nGitHub ref and goes through the folder-seed path (Case B, separate issue)."},"ReviewTarget":{"oneOf":[{"type":"object","required":["branch","type"],"properties":{"branch":{"type":"string"},"type":{"type":"string","enum":["base_branch"]}}},{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["uncommitted_changes"]}}},{"type":"object","required":["sha","type"],"properties":{"sha":{"type":"string"},"title":{"type":["string","null"]},"type":{"type":"string","enum":["commit"]}}},{"type":"object","required":["instructions","type"],"properties":{"instructions":{"type":"string"},"type":{"type":"string","enum":["custom"]}}}],"description":"What the change represents, so the reviewer frames its pass correctly."},"Schedule":{"type":"object","description":"A scheduled agent run. Also the create/get/list response body.","required":["id","owner_sub","agent_id","kickoff","cron","tz","enabled","next_fire_at","created_at","updated_at"],"properties":{"agent_id":{"type":"string","description":"Agent to run. Latest version is resolved at fire time."},"created_at":{"type":"string","format":"date-time"},"cron":{"type":"string","description":"5-field cron expression, e.g. `0 9 * * 1-5`."},"enabled":{"type":"boolean"},"id":{"type":"string","description":"`sched_<uuid>`. Immutable."},"kickoff":{"type":"array","items":{"$ref":"#/components/schemas/TextBlock"},"description":"Seed `user.message` content appended when the run fires."},"last_fired_at":{"type":["string","null"],"format":"date-time"},"next_fire_at":{"type":"string","format":"date-time","description":"UTC instant of the next occurrence — the dispatcher's due-scan key."},"owner_sub":{"type":"string","description":"JWT `sub` of the owning user. Every CRUD op is scoped to it."},"tz":{"type":"string","description":"IANA timezone the cron is interpreted in, e.g. `America/New_York`."},"updated_at":{"type":"string","format":"date-time"}}},"ScheduleList":{"type":"object","description":"`GET /v1/schedules` response.","required":["data","has_more"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Schedule"}},"has_more":{"type":"boolean","description":"Always `false` in v1 — the endpoint returns every schedule the\ncaller owns. Reserved for cursor pagination."}}},"SchedulePatch":{"type":"object","description":"`PATCH /v1/schedules/{id}` body. Every field is optional; absent means\nunchanged. A `cron` or `timezone` change recomputes `next_fire_at`.","properties":{"cron":{"type":["string","null"]},"enabled":{"type":["boolean","null"]},"kickoff":{"type":["array","null"],"items":{"$ref":"#/components/schemas/TextBlock"}},"timezone":{"type":["string","null"]}}},"Session":{"type":"object","description":"A runtime instance of an agent.\n\nState machine: `idle` → `running` (on `user.message`) → `idle` (on\n`end_turn` / `max_tokens` / interrupt / error). `terminated` is the\nunrecoverable end state.","required":["id","agent_id","agent_version","status","created_at"],"properties":{"agent_id":{"type":"string","description":"Agent the session references. Sessions pin an agent + version at\ncreate time so subsequent agent edits don't change the session's\nbehavior mid-run."},"agent_version":{"type":"integer","format":"int32","minimum":0},"archived_at":{"type":["string","null"],"format":"date-time"},"browser_environment_id":{"type":["string","null"],"description":"Companion browser environment for workspace sessions. Browser tools use\nthis instead of `vm_id` so Build/ForWork keep their normal development or\nworkspace VM while web automation runs in the Chrome template."},"browser_vm_id":{"type":["string","null"],"description":"VM id backing [`Self::browser_environment_id`]. Internal execution\ntarget for the browser tool suite; lazily provisioned on first browser\ntool use and cleared when the workspace session is archived/deleted."},"build_cli":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BuildCliSessionState","description":"Durable identity for a local Build CLI projection. Present only when\n`profile == build_cli`; the attached CLI remains the execution worker."}]},"cloned_repos":{"type":"array","items":{"$ref":"#/components/schemas/ClonedRepo"},"description":"Repos pre-cloned into the bound VM at provision (#93), echoed from the\nenvironment so the caller (e.g. the docs driver) knows the in-VM paths\nwithout a `list_dir`. Empty unless the session bound an environment\nwhose VM cloned repos. `#[serde(default)]` keeps older sessions\ndeserializable."},"cost":{"$ref":"#/components/schemas/Cost","description":"Cumulative dollar cost across the session, in micro-USD (#157). This is\na **frozen ledger**: the sum of each completed loop's cost priced at the\ntime it ran, so a later pricing change (`make sync-catwalk`) never\nre-prices past turns — \"what you were billed at the time\". Equals the\nsum of every `session.status_idle` event's `cost`. `#[serde(default)]`\nkeeps pre-#157 sessions deserializable."},"created_at":{"type":"string","format":"date-time"},"deployment_id":{"type":["string","null"],"description":"Deployment that fired this session (#226), stamped by\n`POST /v1/deployments/{id}/run`. `None` for ad-hoc sessions. This is the\nkey the \"runs = filtered sessions\" view + the cross-agent Sessions filter\nread on. `#[serde(default)]` keeps older sessions deserializable."},"environment_id":{"type":["string","null"],"description":"The environment bound to this session. `vm_id` is the execution target\nreturned by binding that environment."},"folder_id":{"type":["string","null"]},"folder_managed":{"type":"boolean","description":"True when `folder_id` is a hidden attachment folder this service\ncreated lazily on first attach (#368) — deleted with the session\n(delete only, never archive: archive is reversible and the next bind\nre-mounts `folder_id`). User-picked folders are never managed."},"id":{"type":"string","description":"`sesn_<uuid>`. Immutable."},"last_message_at":{"type":["string","null"],"format":"date-time"},"model":{"type":["string","null"],"description":"Optional per-session model override for workspace chat turns. The\npinned agent still supplies tools/system prompt; the turn loader swaps\nits model when this is set."},"owner_sub":{"type":["string","null"],"description":"JWT `sub` of the user this session runs on behalf of. Stamped on\ndispatcher-fired scheduled runs for attribution; `None` for sessions\ncreated directly via the HTTP API. `#[serde(default)]` keeps\nalready-persisted sessions (written before this field existed)\ndeserializable."},"profile":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SessionProfile","description":"Product profile for shared first-party session surfaces. `workspace`\npowers Build/ForWork; absent for ordinary managed-agent sessions."}]},"project_id":{"type":["string","null"],"description":"External product references. managed-agents owns the session/runtime;\nproject and folder services remain the source of truth for these ids."},"status":{"$ref":"#/components/schemas/SessionStatus"},"surface":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/WorkspaceSurface","description":"Product grouping inside a profile. For `workspace`, this is `build` or\n`for_work`; absent for generic sessions."}]},"title":{"type":["string","null"],"description":"Human-visible title used by workspace sidebars. Optional so old rows and\ngeneric sessions keep their existing wire shape."},"updated_at":{"type":"string","format":"date-time"},"usage":{"$ref":"#/components/schemas/Usage","description":"Cumulative token usage across every model call in the session.\nMirrors Anthropic's session-level shape."},"vault_ids":{"type":"array","items":{"type":"string"},"description":"Vaults attached at create (#142 M4): the credential collections the\nsession may present to MCP servers. At connect time the FIRST attached\nvault holding a credential whose `mcp_server_url` exactly matches the\nserver's url wins. The caller must own every vault (checked at create,\n404 otherwise). `#[serde(default)]` keeps older sessions deserializable."},"vm_id":{"type":["string","null"],"description":"VM bound to this session for in-VM tool execution (#68). The `shell`\ntool dispatches into this VM. `None` = no exec environment. Set from\n`CreateSessionRequest`; `#[serde(default)]` keeps sessions persisted\nbefore this field existed deserializable."},"workspace_path":{"type":["string","null"],"description":"Where infra mounted the session's `folder_id` inside the bound VM,\nechoed from the environment at bind. The workspace turn appends it to\nthe agent's system prompt — the model has no other way to learn the\nfolder's in-VM location. `#[serde(default)]` keeps older sessions\ndeserializable."}}},"SessionChatRequest":{"type":"object","description":"POST `/v1/sessions/{id}/chat` body. This is the AI SDK\n`DefaultChatTransport` persisted shape: the latest user message plus\noptional model override.","required":["message"],"properties":{"id":{"type":["string","null"]},"message":{"$ref":"#/components/schemas/UiMessage"},"messageId":{"type":["string","null"]},"model":{"type":["string","null"]},"reasoning_effort":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ReasoningEffort"}]},"trigger":{"type":["string","null"]}}},"SessionList":{"type":"object","description":"`GET /v1/sessions` response.","required":["data","has_more"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SessionListItem"}},"has_more":{"type":"boolean","description":"Always `false` in v1."}}},"SessionListItem":{"allOf":[{"$ref":"#/components/schemas/Session"},{"type":"object","properties":{"worker":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BuildCliWorkerStatus","description":"Present only for Build CLI sessions. Disconnected summaries retain the\nlatest durable non-secret worker metadata."}]}}}],"description":"Flat session summary returned by `GET /v1/sessions`."},"SessionProfile":{"type":"string","enum":["workspace","build_cli"]},"SessionStatus":{"oneOf":[{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["idle"]},"stop_reason":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/StopReason","description":"Why the agent stopped. `None` while a session has never run."}]}}},{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["running"]}}},{"type":"object","description":"Reserved — emitted by the spec on transient retries (`session.error`\nwith `retry_status: \"retrying\"`). The v1 runtime doesn't retry yet;\nkept in the wire-shape so clients written against the Anthropic\nspec deserialize cleanly when we add retries later.","required":["status"],"properties":{"status":{"type":"string","enum":["rescheduling"]}}},{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["terminated"]}}}],"description":"Session status. Serializes as a flat string when no payload is attached\n(`\"running\"`, `\"terminated\"`) and as a tagged object for `idle` (carries\nthe optional `stop_reason`).\n\nWe use an internally tagged enum so the wire shape matches the Managed\nAgents pattern: `{\"status\": \"running\"}` / `{\"status\": \"idle\",\n\"stop_reason\": {\"type\": \"end_turn\"}}`."},"StopReason":{"oneOf":[{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["end_turn"]}}},{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["max_tokens"]}}},{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["interrupted"]}}},{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["error"]}}}]},"TextBlock":{"oneOf":[{"type":"object","required":["text","type"],"properties":{"text":{"type":"string"},"type":{"type":"string","enum":["text"]}}},{"type":"object","required":["url","type"],"properties":{"media_type":{"type":["string","null"]},"type":{"type":"string","enum":["file"]},"url":{"type":"string"}}}],"description":"Content blocks used for user input, agent output, and tool-result bodies."},"TokenEndpointAuthRequest":{"oneOf":[{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["none"]}}},{"type":"object","required":["client_secret","type"],"properties":{"client_secret":{"type":"string"},"type":{"type":"string","enum":["client_secret_basic"]}}},{"type":"object","required":["client_secret","type"],"properties":{"client_secret":{"type":"string"},"type":{"type":"string","enum":["client_secret_post"]}}}],"description":"Inbound client-auth mode for the refresh call (#221). `client_secret` is\nplaintext on the request only — write-only."},"TokenSourceProvider":{"type":"string","description":"The service identity a `token_source` server mints against. Maps to an\n`agent-core` `TokenSource` in M2; named (not secret) so it's safe on the\nagent row.","enum":["supabase","hydra"]},"ToolInfo":{"type":"object","description":"One registered tool, projected from the boot-time\n[`ToolRegistry`](crate::tools::ToolRegistry) for `GET /v1/tools`.","required":["name","description","schema"],"properties":{"description":{"type":"string","description":"The docstring handed to the LLM (and to the console's tool picker)."},"name":{"type":"string","description":"Stable identifier referenced by `Agent.tools[].name`."},"schema":{"description":"JSON Schema for the tool's `input` argument — the same schema\nforwarded to the model as `ToolDefinition.parameters`."}}},"ToolList":{"type":"object","description":"`GET /v1/tools` response.\n\nUses the managed-agents `{data, has_more}` list convention. The model catalog\ndeliberately uses the shared OpenAI-style `{object: \"list\", data}` response.","required":["data","has_more"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ToolInfo"},"description":"Every registered tool, sorted by name."},"has_more":{"type":"boolean","description":"Always `false` — the registry is fixed at boot and small."}}},"ToolRef":{"type":"object","description":"A reference to a tool by registered name. The runtime resolves the impl\nat request build time.","required":["name"],"properties":{"name":{"type":"string"}}},"ToolResultBlock":{"oneOf":[{"type":"object","required":["text","type"],"properties":{"text":{"type":"string"},"type":{"type":"string","enum":["text"]}}}],"description":"Tool-result content blocks.\n\nCurrently shape-identical to `TextBlock` but kept as a distinct type\nbecause Anthropic's spec is likeliest to grow the tool-result content\nchannel first (images, structured outputs); a single-variant enum here\nlets that expansion happen by adding new variants instead of widening\n`TextBlock` and redirecting every caller."},"Trigger":{"type":"object","description":"An auto-lane trigger: fire `agent_id` when one of `events` arrives for\n`repo`. Also the create/get/list response body.","required":["id","owner_sub","agent_id","source","repo","events","enabled","created_at","updated_at"],"properties":{"agent_id":{"type":"string","description":"Agent to run. Latest version is resolved at fire time."},"created_at":{"type":"string","format":"date-time"},"enabled":{"type":"boolean"},"events":{"type":"array","items":{"type":"string"},"description":"Non-empty subset of [`ALLOWED_EVENTS`]."},"id":{"type":"string","description":"`trg_<uuid>`. Immutable."},"kickoff_template":{"type":["string","null"],"description":"Optional kickoff override. `{{repo}}`/`{{pr}}`/`{{branch}}`/`{{path}}`/\n`{{sha}}`/`{{instruction}}` placeholders are rendered at fire time (PR 2);\nabsent → the built-in neutral kickoff."},"owner_sub":{"type":"string","description":"JWT `sub` of the owning user. Every CRUD op is scoped to it."},"repo":{"type":"string","description":"`owner/name`, matched verbatim against the normalized event's repo."},"source":{"type":"string","description":"Event source; `github` is the only v1 value (kept as data so\nnon-GitHub sources can slot in without reshaping)."},"updated_at":{"type":"string","format":"date-time"}}},"TriggerFire":{"type":"object","description":"One webhook-delivery → agent-run record. Also the dedup ledger row and\nthe `GET /v1/triggers/{id}/fires` response body.","required":["id","dedup_key","delivery_id","lane","repo","pr_number","payload","status","created_at","updated_at"],"properties":{"created_at":{"type":"string","format":"date-time"},"dedup_key":{"type":"string","description":"Immutable at-most-once key: `{delivery_id}:{trigger_id}` (auto) or\n`{delivery_id}:mention` (mention). Survives trigger deletion."},"delivery_id":{"type":"string","description":"`X-GitHub-Delivery` GUID, verbatim."},"environment_id":{"type":["string","null"]},"error":{"type":["string","null"]},"id":{"type":"string","description":"`fire_<uuid>`. Immutable."},"lane":{"$ref":"#/components/schemas/FireLane"},"mode":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/FireMode","description":"Mention-lane only; the auto lane's agent comes from the trigger row."}]},"payload":{"description":"The [`NormalizedHookEvent`] as received, for debugging/replay."},"pr_number":{"type":"integer","format":"int64"},"repo":{"type":"string"},"session_id":{"type":["string","null"]},"status":{"$ref":"#/components/schemas/FireStatus"},"trigger_id":{"type":["string","null"],"description":"The auto-lane trigger that fired, if any. `None` for mention fires\nand for fires whose trigger was deleted afterwards (FK `SET NULL`)."},"updated_at":{"type":"string","format":"date-time"}}},"TriggerList":{"type":"object","description":"`GET /v1/triggers` response.","required":["data","has_more"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Trigger"}},"has_more":{"type":"boolean","description":"Always `false` in v1 — reserved for cursor pagination."}}},"TriggerPatch":{"type":"object","description":"`PATCH /v1/triggers/{id}` body. Every field is optional; absent means\nunchanged.","properties":{"enabled":{"type":["boolean","null"]},"events":{"type":["array","null"],"items":{"type":"string"}},"kickoff_template":{"type":["string","null"],"description":"Send an **empty string** to clear the override back to the built-in\nkickoff. A JSON `null` is treated as \"absent / unchanged\" (it can't\nbe told apart from an omitted field), so it does NOT clear."},"repo":{"type":["string","null"]}}},"UiMessage":{"type":"object","description":"One `UIMessage` (a turn). `parts` carries the typed content.","required":["role"],"properties":{"id":{"type":["string","null"]},"parts":{"type":"array","items":{"$ref":"#/components/schemas/UiPart"}},"role":{"type":"string","example":"user"}}},"UiPart":{"type":"object","description":"One `UIMessage` part.\n\nDeserialized as a flat permissive struct rather than a tagged enum: the AI\nSDK `tool-<name>` part type embeds the tool name in the discriminant, which\nserde's `#[serde(tag)]` can't match. Adapters dispatch on `kind` and read\nonly the fields relevant to that type. Unknown fields are tolerated on\ninput; on output, `None` fields are omitted entirely.","required":["type"],"properties":{"errorText":{"type":["string","null"]},"fileId":{"type":["string","null"],"description":"Cluster storage-api durable file id carried by web/mobile file parts."},"filename":{"type":["string","null"],"description":"Original filename carried by web/mobile `file` parts. Workspace chat\nuses it to point the agent at the synced copy inside the work folder\n(#368); absent on parts that never had one."},"input":{"type":"object"},"mediaType":{"type":["string","null"]},"output":{"type":"object"},"providerExecuted":{"type":["boolean","null"],"description":"`true` on server-executed tool parts (this workspace's chat surface\nemits it; the persistence writer must round-trip it so reloaded\nhistory renders as server-run, not client-dispatchable)."},"providerMetadata":{"type":"object"},"state":{"type":["string","null"],"description":"Tool-part lifecycle: `input-available` | `output-available` | `output-error`."},"text":{"type":["string","null"]},"toolCallId":{"type":["string","null"]},"type":{"type":"string","description":"`text` | `file` | `reasoning` | `tool-<name>` | `step-start` | …\n(each service decides which it accepts).","example":"text"},"url":{"type":["string","null"]}}},"UpdateCredentialAuthRequest":{"oneOf":[{"type":"object","required":["type"],"properties":{"mcp_server_url":{"type":["string","null"],"description":"Immutable structural key — optional. If present it MUST equal the\nstored url (a different value is rejected; archive + recreate to change)."},"token":{"type":["string","null"],"description":"New plaintext bearer. Write-only: re-encrypted before storage, never\nreturned. Omit to keep the current secret (e.g. a pure rename)."},"type":{"type":"string","enum":["static_bearer"]}}},{"type":"object","description":"Deser-only (#232): an `mcp_oauth` rotate is archive + recreate (its access\ntoken auto-refreshes; its refresh token rotates at re-auth). Fields are\ndropped — the handler returns the pointed 400.","required":["type"],"properties":{"type":{"type":"string","enum":["mcp_oauth"]}}},{"type":"object","description":"Deser-only (#232): an `environment_variable` rotate is archive + recreate.","required":["type"],"properties":{"type":{"type":"string","enum":["environment_variable"]}}},{"type":"object","description":"Deser-only (#215/#232): a `static_headers` rotate is archive + recreate\n(no in-place header-map rotation in v1). Fields dropped — handler 400s.","required":["type"],"properties":{"type":{"type":"string","enum":["static_headers"]}}}],"description":"Inbound credential rotate payload (plaintext token).\n\nTagged `type` like the create enum. **Rotation is `static_bearer`-only** — the\n`mcp_oauth` + `environment_variable` arms exist **only so a spec-conformant PUT\nof those types deserializes** (yielding the pointed 400 in\n[`resolve_token_rotation`](crate::api::vaults::credentials), not a serde 422,\n#232). They are **empty** struct variants: any secret in the body is dropped at\ndeserialization (never captured), so unlike the create enum there is nothing to\nredact — strictly safer than holding the fields. Structural fields on\n`static_bearer` are immutable: `mcp_server_url` may be echoed but a DIFFERENT\nvalue is a 400. `Debug` stays hand-written to REDACT `static_bearer`'s token."},"UpdateCredentialRequest":{"type":"object","description":"PUT `/v1/vaults/{id}/credentials/{cid}` body (#220): rotate the secret + rename.\n\nThe token (in `auth`) is **plaintext on the request only** — encrypted before\nstorage, never echoed back. **Secret-bearing → extract with `ApiJsonRedacted`,\nnever plain `ApiJson`** (#231 — see `CreateCredentialRequest`).","properties":{"auth":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/UpdateCredentialAuthRequest"}]},"display_name":{"type":["string","null"]}}},"UpdateVaultRequest":{"type":"object","description":"PUT `/v1/vaults/{id}` body (#220).\n\nBoth fields optional — omit to keep the current value; `metadata: {}` clears\nall metadata (replace semantics, no tri-state). Structural identity (`id`,\n`owner`) is immutable.","properties":{"display_name":{"type":["string","null"]},"metadata":{"type":["object","null"],"additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}}}},"Usage":{"type":"object","description":"Cumulative token usage across a session's model calls.\n\nFields match Anthropic's wire shape exactly so the JSON drops straight\nthrough to clients building cost dashboards. Cache fields are `Option`\n(not `u64` defaulting to 0) so the wire distinguishes \"no cache\nactivity measured\" from \"cache hit/miss of 0 tokens\".","properties":{"cache_creation_input_tokens":{"type":["integer","null"],"format":"int64","minimum":0},"cache_read_input_tokens":{"type":["integer","null"],"format":"int64","minimum":0},"input_tokens":{"type":"integer","format":"int64","minimum":0},"output_tokens":{"type":"integer","format":"int64","minimum":0}}},"UserInputEvent":{"oneOf":[{"type":"object","required":["content","type"],"properties":{"content":{"type":"array","items":{"$ref":"#/components/schemas/TextBlock"}},"mcp_services":{"type":"array","items":{"type":"string"},"description":"Curated hosted MCP service ids selected for this message only."},"type":{"type":"string","enum":["user.message"]}}},{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["user.interrupt"]}}}],"description":"Subset of [`Event`] accepted from clients on POST events.\nServer-assigned fields (`id`, `processed_at`) are minted by the\nhandler from the event's own ingest time."},"ValidationStatus":{"type":"string","description":"`valid`: the token works (no action). `invalid`: the grant is gone / a 4xx —\nre-authorize. `unknown`: transient (5xx / 429 / network) — retry.","enum":["valid","invalid","unknown"]},"VaultCredentialList":{"type":"object","description":"`GET /v1/vaults/{id}/credentials` response (newest-first, archived excluded\nunless `include_archived=true`).","required":["data","has_more"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/VaultCredentialResponse"}},"has_more":{"type":"boolean","description":"`true` when more records matched than fit in this page's `limit` (#220)."}}},"VaultCredentialResponse":{"type":"object","description":"Credential response — **structurally** omits the secret (write-only).\n\nCarries the category key (`mcp_server_url` for MCP creds, `secret_name` for\nan environment-variable cred) + a `type` discriminator, but never the secret.","required":["id","vault_id","display_name","type","created_at","updated_at"],"properties":{"archived_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"header_names":{"type":["array","null"],"items":{"type":"string"},"description":"The header NAMES — present for a `static_headers` credential (#215),\nabsent otherwise. The values are write-only and never appear here."},"id":{"type":"string"},"mcp_server_url":{"type":["string","null"],"description":"The MCP server url — present for MCP credentials, absent for an\n`environment_variable` credential."},"secret_name":{"type":["string","null"],"description":"The injected env-var name — present for an `environment_variable`\ncredential (#222), absent for MCP credentials."},"type":{"type":"string","description":"`static_bearer` | `mcp_oauth` | `environment_variable` (#222)."},"updated_at":{"type":"string","format":"date-time"},"vault_id":{"type":"string"}}},"VaultList":{"type":"object","description":"`GET /v1/vaults` response (newest-first, archived excluded unless\n`include_archived=true`).","required":["data","has_more"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/VaultResponse"}},"has_more":{"type":"boolean","description":"`true` when more records matched than fit in this page's `limit` (#220)."}}},"VaultResponse":{"type":"object","description":"Vault response — projects [`Vault`], omitting the internal `owner`.","required":["id","display_name","metadata","created_at","updated_at"],"properties":{"archived_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"display_name":{"type":"string"},"id":{"type":"string"},"metadata":{"type":"object","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"updated_at":{"type":"string","format":"date-time"}}},"WebhookCreated":{"type":"object","description":"`POST /v1/webhooks` response — the ONLY place the signing `secret` is ever\nreturned. Configure your Svix/Anthropic-SDK verifier with it now; it is\nnever disclosed again.","required":["id","url","events","enabled","secret","created_at","updated_at"],"properties":{"created_at":{"type":"string","format":"date-time"},"enabled":{"type":"boolean"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"}},"id":{"type":"string"},"secret":{"type":"string","description":"The `whsec_`-prefixed signing secret. Shown once — store it."},"updated_at":{"type":"string","format":"date-time"},"url":{"type":"string"}}},"WebhookEventType":{"type":"string","description":"The vault/credential lifecycle events a subscription can carry.\n\nThe wire form is the Anthropic-aligned dotted string (serde-renamed), so an\nunknown value on a create request fails deserialization → a clean 400.","enum":["vault.archived","vault.deleted","vault_credential.archived","vault_credential.deleted"]},"WebhookList":{"type":"object","description":"`GET /v1/webhooks` response.","required":["data","has_more"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookResponse"}},"has_more":{"type":"boolean","description":"Always `false` in v1 (every subscription the caller owns is returned)."}}},"WebhookPatch":{"type":"object","description":"`PATCH /v1/webhooks/{id}` body. Absent fields are unchanged. The secret is\nnot rotatable in v1 (delete + recreate to roll it).","properties":{"enabled":{"type":["boolean","null"]},"events":{"type":["array","null"],"items":{"$ref":"#/components/schemas/WebhookEventType"}},"url":{"type":["string","null"]}}},"WebhookResponse":{"type":"object","description":"Webhook subscription as returned by GET/list/patch — NO secret.","required":["id","url","events","enabled","created_at","updated_at"],"properties":{"created_at":{"type":"string","format":"date-time"},"enabled":{"type":"boolean"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"}},"id":{"type":"string"},"updated_at":{"type":"string","format":"date-time"},"url":{"type":"string"}}},"WorkspaceLifecycleStatus":{"type":"string","enum":["provisioning","resuming","ready","failed"]},"WorkspaceSurface":{"type":"string","enum":["build","for_work"]}},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Hydra RS256 access token for managed-agents clients; HS256 Supabase user tokens are also accepted when SUPABASE_JWT_SECRET is configured. MCP service credentials are owner-scoped to the verified token `sub`."}}},"tags":[{"name":"Agents","description":"Agent definitions (CRUD + versioning + archive)"},{"name":"Sessions","description":"Session lifecycle (create + read + archive + delete)"},{"name":"Environments","description":"Cloud environments (Firecracker VM wrappers; CRUD + archive). A session binds one for in-VM tool execution."},{"name":"Deployments","description":"Named, reusable run-configs (#226): agent + environment + vaults + kickoff, run manually via POST /v1/deployments/{id}/run (on-behalf-of the caller). A deployment's runs are its sessions filtered by deployment_id."},{"name":"Events","description":"Event log (POST user events, GET history, SSE stream)"},{"name":"Schedules","description":"Scheduled agent runs (cron + timezone, tenant-scoped)"},{"name":"Triggers","description":"GitHub event triggers (#146): auto-lane opt-in config + per-delivery fire records, tenant-scoped"},{"name":"Reviews","description":"On-demand manual agent runs (#165): provision an ephemeral VM with repos pre-cloned, run a VM-backed agent (built-in reviewers or a custom agent), tear down when done. On-behalf-of the caller."},{"name":"Tools","description":"Read-only registry of tools agents can reference by name"},{"name":"Models","description":"Read-only catalog of supported models + reasoning capabilities"},{"name":"Vaults","description":"Per-end-user MCP credential vaults (#142 M4): vault + credential CRUD; tokens are write-only + encrypted at rest. Attach via session vault_ids."},{"name":"Webhooks","description":"Vault/credential lifecycle webhooks (#223): subscribe a URL to vault/credential archive+delete events. Svix-signed deliveries; the signing secret is returned ONCE at create. Tenant-scoped."}]}