@soat/cli 0.78.0 → 0.79.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +2 -2
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
2
|
import{createServer}from"node:http";import*as path from"node:path";import{fileURLToPath}from"node:url";import input from"@inquirer/input";import password from"@inquirer/password";import*as sdk from"@soat/sdk";import{createClient,createConfig}from"@soat/sdk";import{program}from"commander";import*as fs from"node:fs";import{load}from"js-yaml";import*as os from"node:os";import{createHmac,timingSafeEqual}from"node:crypto";//#region package.json
|
|
3
|
-
var version="0.
|
|
3
|
+
var version="0.79.1";//#endregion
|
|
4
4
|
//#region src/cli-wrappers/wrappers/formations.ts
|
|
5
5
|
var FORMATION_COMMANDS=["validate-formation","plan-formation","create-formation","update-formation"];/**
|
|
6
6
|
* The two commands that *deploy*. `validate-formation` and `plan-formation`
|
|
@@ -52,7 +52,7 @@ var WRAPPERS=[formationsWrapper];var resolveWrapperForCommand=args=>{const{comma
|
|
|
52
52
|
//#region src/config.ts
|
|
53
53
|
var CONFIG_FILE=path.join(os.homedir(),".soat","config.json");var readConfig=()=>{try{return JSON.parse(fs.readFileSync(CONFIG_FILE,"utf8"));}catch{return{};}};var writeProfile=(name,profile)=>{const config=readConfig();config[name]=profile;fs.mkdirSync(path.dirname(CONFIG_FILE),{recursive:true});fs.writeFileSync(CONFIG_FILE,JSON.stringify(config,null,2));};var resolveClient=profileName=>{const envBaseUrl=process.env["SOAT_BASE_URL"];const envToken=process.env["SOAT_TOKEN"];if(envBaseUrl&&envToken)return createClient(createConfig({baseUrl:envBaseUrl,headers:{Authorization:`Bearer ${envToken}`}}));const name=profileName??process.env["SOAT_PROFILE"]??"default";const profile=readConfig()[name];if(profile)return createClient(createConfig({baseUrl:envBaseUrl??profile.baseUrl,headers:{Authorization:`Bearer ${profile.token}`}}));if(envBaseUrl)return createClient(createConfig({baseUrl:envBaseUrl}));console.error(`Profile "${name}" not found. Run: soat configure${name!=="default"?` --profile ${name}`:""}`);process.exit(1);};//#endregion
|
|
54
54
|
//#region src/generated/routes.ts
|
|
55
|
-
var routes={"list-activity":{serviceClass:"Activity",operationId:"listActivity",description:"Returns activity entries for a project, newest first, filterable by kind, severity, agent, generation and orchestration run — every filter composable with the rest. Paginated with an opaque cursor rather than offset/limit — pass the previous page's `next_cursor` to fetch the next one; a `null` `next_cursor` means there is no more data.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/activity",httpMethod:"get",pathParams:[],queryParams:["project_id","kind","severity","agent_id","generation_id","orchestration_run_id","cursor","limit"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"kind","description":"Filter by activity kind","required":false,"type":"string","in":"query"},{"name":"severity","description":"Filter by severity","required":false,"type":"string","in":"query"},{"name":"agent_id","description":"Filter to entries produced by one agent. Composable with every other filter — entries carry one agent, one generation and one run, so naming two narrows to the rows where both hold.","required":false,"type":"string","in":"query"},{"name":"generation_id","description":"Filter to entries produced during one agent generation. This is what turns `tool_resolution_failed` from alertable into usable: the warning for a suspect turn is one query rather than a scan of the project's feed.","required":false,"type":"string","in":"query"},{"name":"orchestration_run_id","description":"Filter to entries produced during one orchestration run.","required":false,"type":"string","in":"query"},{"name":"cursor","description":"Opaque cursor from a previous page's `next_cursor`","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"export-activity":{serviceClass:"Activity",operationId:"exportActivity",description:"Streams a project's activity entries as newline-delimited JSON — one entry object per line, **oldest first**, where the feed itself reads newest first: a feed answers what just happened, and a file is read start to end. `project_id` is required: the export is per-project by design. Filters behave exactly as they do on the list endpoint.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/activity",httpMethod:"get",pathParams:[],queryParams:["project_id","kind","severity","agent_id","generation_id","orchestration_run_id"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project whose activity is exported","required":true,"type":"string","in":"query"},{"name":"kind","description":"Only entries of this kind","required":false,"type":"string","in":"query"},{"name":"severity","description":"Only entries of this severity","required":false,"type":"string","in":"query"},{"name":"agent_id","description":"Only entries an agent produced","required":false,"type":"string","in":"query"},{"name":"generation_id","description":"Only entries a generation produced","required":false,"type":"string","in":"query"},{"name":"orchestration_run_id","description":"Only entries an orchestration run produced","required":false,"type":"string","in":"query"}]},"list-actors":{serviceClass:"Actors",operationId:"listActors",description:"Returns all actors the caller has access to. If projectId is provided, returns only actors in that project. project keys are scoped to a single project automatically. JWT users without projectId receive actors across all their accessible projects.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/actors",httpMethod:"get",pathParams:[],queryParams:["project_id","external_id","name","agent_id","chat_id","conversation_id","tags","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (optional)","required":false,"type":"string","in":"query"},{"name":"external_id","description":"External ID to filter by (e.g. WhatsApp phone number)","required":false,"type":"string","in":"query"},{"name":"name","description":"Case-insensitive substring match on the actor name","required":false,"type":"string","in":"query"},{"name":"agent_id","description":"Return only actors linked to this agent","required":false,"type":"string","in":"query"},{"name":"chat_id","description":"Return only actors linked to this chat","required":false,"type":"string","in":"query"},{"name":"conversation_id","description":"Return only actors that participate in this conversation (derived from the conversation's messages).\n","required":false,"type":"string","in":"query"},{"name":"tags","description":"Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n","required":false,"type":"array","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-actor":{serviceClass:"Actors",operationId:"createActor",description:"Creates a new actor. project keys automatically infer the project from the key's scope; JWT callers must supply projectId.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/actors",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID. Required for JWT auth; omit when using an project key.","required":false,"type":"string","in":"body"},{"name":"name","description":"","required":true,"type":"string","in":"body"},{"name":"external_id","description":"Optional external identifier (e.g. WhatsApp phone number). If provided and an actor with this externalId already exists in the project, the existing actor is returned (idempotent — 200 OK).","required":false,"type":"string","in":"body"},{"name":"instructions","description":"Persona-specific instructions composed into the effective system prompt during conversation generation.","required":false,"type":"string","in":"body"},{"name":"agent_id","description":"Agent to link this actor to. Mutually exclusive with chat_id.","required":false,"type":"string","in":"body"},{"name":"chat_id","description":"Chat to link this actor to. Mutually exclusive with agent_id.","required":false,"type":"string","in":"body"}]},"get-actor":{serviceClass:"Actors",operationId:"getActor",description:"Returns an actor by its ID",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/actors",httpMethod:"get",pathParams:["actor_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"actor_id","description":"Actor ID","required":true,"type":"string","in":"path"}]},"update-actor":{serviceClass:"Actors",operationId:"updateActor",description:"Updates an actor's properties",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/actors",httpMethod:"patch",pathParams:["actor_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"actor_id","description":"Actor ID","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"external_id","description":"External identifier (e.g. WhatsApp phone number)","required":false,"type":"string","in":"body"},{"name":"instructions","description":"Persona-specific instructions","required":false,"type":"string","in":"body"},{"name":"agent_id","description":"Agent to link this actor to. Mutually exclusive with chat_id.","required":false,"type":"string","in":"body"},{"name":"chat_id","description":"Chat to link this actor to. Mutually exclusive with agent_id.","required":false,"type":"string","in":"body"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":false,"type":"object","in":"body"}]},"delete-actor":{serviceClass:"Actors",operationId:"deleteActor",description:"Deletes an actor by its ID",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/actors",httpMethod:"delete",pathParams:["actor_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"actor_id","description":"Actor ID","required":true,"type":"string","in":"path"}]},"get-actor-tags":{serviceClass:"Actors",operationId:"getActorTags",description:"Returns all tags attached to the actor",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/actors",httpMethod:"get",pathParams:["actor_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"actor_id","description":"Actor ID","required":true,"type":"string","in":"path"}]},"replace-actor-tags":{serviceClass:"Actors",operationId:"replaceActorTags",description:"Replaces all tags on the actor with the provided tags (not merged)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/actors",httpMethod:"put",pathParams:["actor_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"actor_id","description":"Actor ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"merge-actor-tags":{serviceClass:"Actors",operationId:"mergeActorTags",description:"Merges provided tags into the actor's existing tags (existing tags are preserved unless overridden)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/actors",httpMethod:"patch",pathParams:["actor_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"actor_id","description":"Actor ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"list-agents":{serviceClass:"Agents",operationId:"listAgents",description:"Returns all agents in the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID to filter by","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-agent":{serviceClass:"Agents",operationId:"createAgent",description:"Creates a new agent bound to an AI provider.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project","required":false,"type":"string","in":"body"},{"name":"ai_provider_id","description":"Public ID of the AI provider to pin. Mutually exclusive with `model_route_id`.","required":false,"type":"string","in":"body"},{"name":"model_route_id","description":"Public ID of a model route in the same project. The agent's completion model is then resolved through the route's ordered targets with failover. Mutually exclusive with `ai_provider_id` and `model`.","required":false,"type":"string","in":"body"},{"name":"name","description":"Display name.","required":false,"type":"string","in":"body"},{"name":"instructions","description":"System instructions, sent as the system message of every generation.","required":false,"type":"string","in":"body"},{"name":"model","description":"Model identifier on the pinned AI provider. Omitted, the provider's default model is used. Cannot be combined with `model_route_id`.","required":false,"type":"string","in":"body"},{"name":"tool_bindings","description":"Tools to attach, one binding object per tool — the only attachment field. An entry is either a reference (`{ \"tool_id\": … }`) or an inline definition (`{ \"tool\": … }`). See [Tool Bindings](/docs/modules/agents#tool-bindings).","required":false,"type":"array","in":"body"},{"name":"max_steps","description":"Maximum agent loop steps per turn (default `20`).","required":false,"type":"integer","in":"body"},{"name":"tool_choice","description":"Tool choice strategy. Accepts a string (`\"auto\"`, `\"required\"`) or an object (`{ \"type\": \"tool\", \"tool_name\": \"my_tool\" }`). A forcing value (`\"required\"` or the object form) forbids a final assistant message on every step of every turn, including a resumed or continued one, so it requires a `has_tool_call` entry in `stop_conditions` — otherwise the write is refused with `FORCED_TOOL_CHOICE_CANNOT_STOP`.","required":false,"type":"unknown","in":"body"},{"name":"stop_conditions","description":"Conditions that end the agent's work early, on top of `max_steps`. Two scopes:\n\n`{\"type\": \"has_tool_call\", \"tool_name\": \"<resolved tool name>\"}` ends the **turn** after the step that calls the named tool. It narrows when the loop ends — it never lets it run past `max_steps`.\n\n`{\"type\": \"max_chain_generations\", \"max_generations\": <n>}` bounds the **continuation chain** instead: once the chain has spawned that many generations, further resumptions stop with `chain_limit` rather than extending it. It never shortens a turn. The effective ceiling is the smaller of this and the deployment's `MAX_CONTINUATION_CHAIN_GENERATIONS`, so an agent can be stricter than the platform but never looser.\n\nAn unknown `type`, a `has_tool_call` without a `tool_name`, a `max_chain_generations` whose `max_generations` is not a positive integer, or a non-object entry is rejected with 400.","required":false,"type":"array","in":"body"},{"name":"active_tool_ids","description":"Persisted tools from `tool_bindings` the model sees on every step. Omitted or `[]` leaves every bound tool active. See [Active Tools](/docs/modules/agents#active-tools).","required":false,"type":"array","in":"body"},{"name":"guardrail_ids","description":"Guardrails attached at the agent scope.","required":false,"type":"array","in":"body"},{"name":"step_rules","description":"Per-step overrides of `tool_choice` and `active_tool_ids`, numbered from the first step of the turn. See [Step Rules](/docs/modules/agents#step-rules).","required":false,"type":"array","in":"body"},{"name":"boundary_policy","description":"Restricts which SOAT actions the agent may invoke. Evaluated as the intersection with the caller's own policy, so it can only narrow.","required":false,"type":"object","in":"body"},{"name":"temperature","description":"Sampling temperature passed to the model. Omitted, the provider's default applies.","required":false,"type":"number","in":"body"},{"name":"knowledge_config","description":"Knowledge search run before every generation; its matches are prepended as reference context. See [Knowledge Config](/docs/modules/agents#knowledge-config).","required":false,"type":"object","in":"body"},{"name":"output_schema","description":"JSON Schema describing the structured object the model must return. When set, non-streaming generations constrain output to this schema and the parsed value is returned as `output.object`. The schema is enforced on the way back, not just sent to the model: an object that violates it fails the generation with 502 `OUTPUT_SCHEMA_VALIDATION_FAILED`, naming the violated field. Constraints beyond `required`/`type` (`minLength`, `enum`, `pattern`, `minItems`) are honored and are what reject a structurally valid but degenerate answer. See the Structured Output section in the Agents module docs.","required":false,"type":"object","in":"body"},{"name":"prompt_caching","description":"Prompt caching for this agent's turns. `{\"enabled\": true}` marks two cache breakpoints: the end of the system block, which covers the tool definitions and the instructions, and the last message of each step, which covers the conversation so far. A provider that caches by explicit breakpoint then reads, on every later step and every later turn of the session, the whole prefix the previous one sent, and writes only what was appended. Null or omitted is off: a cache write costs more than an uncached token, so an agent whose prefix is never re-read would pay for the privilege. A prefix below the model's minimum cacheable length is not cached. Cache reads are reported as `cached_tokens` and cache writes as `cache_write_tokens` on usage; a write with no `cache_write_tokens` price bills at the input rate.","required":false,"type":"object","in":"body"},{"name":"max_context_messages","description":"Maximum number of recent messages included in the context window. Null means no limit.","required":false,"type":"integer","in":"body"},{"name":"max_output_tokens","description":"Maximum number of tokens the model may generate per step of a turn. A response that reaches it stops with `stop_reason` `length`. Null leaves the model's default.","required":false,"type":"integer","in":"body"},{"name":"reasoning","description":"How much the model reasons before answering. Translated per provider (Anthropic thinking, Gemini thinking config, OpenAI reasoning effort) against what the model supports; a model that cannot reason ignores it. `none` turns reasoning off where the model allows it. Reasoning tokens are metered as output. Null leaves the model's default.","required":false,"type":"object","in":"body"},{"name":"single_session_per_actor","description":"When true, only one open session per actor_id is allowed for this agent.","required":false,"type":"boolean","in":"body"},{"name":"trace_content_mode","description":"Zero-retention opt-in for this agent. `null` inherits the project's setting; `none` means trace and generation content is never written. Setting `full` under a project whose own mode is `none` is refused with 400 — the project is a floor an agent may only tighten.","required":false,"type":"string","in":"body"},{"name":"on_approval_expiry","description":"What happens when one of this agent's held tool calls expires un-approved. `null` (the default) and `terminate` end the chain there — the expired approval, its `approvals.expired` event and the auto-filed `approval_expired` exception are the whole record. `react` spawns a continuation that reports the staleness to the agent, for an agent that acts on it.","required":false,"type":"string","in":"body"},{"name":"version_label","description":"Optional tag for the config version this write archives (e.g. `initial`). Annotates the version only — it is not stored on the agent and is not part of the config, so labelling a change is never itself a change.","required":false,"type":"string","in":"body"}]},"get-agent":{serviceClass:"Agents",operationId:"getAgent",description:"Returns a single agent by ID. A credential scoped to a project the agent is shared with, through an accepted share, reads its `id` and `name` only.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"get",pathParams:["agent_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"}]},"update-agent":{serviceClass:"Agents",operationId:"updateAgent",description:"Updates an existing agent. Identical to PATCH — both perform partial updates.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"put",pathParams:["agent_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"ai_provider_id","description":"Public ID of the AI provider to pin. Mutually exclusive with `model_route_id`; set to `null` when switching to a route.","required":false,"type":"string","in":"body"},{"name":"model_route_id","description":"Model route in the same project. Mutually exclusive with `ai_provider_id` and `model`; set to null to clear.","required":false,"type":"string","in":"body"},{"name":"name","description":"Display name; `null` clears it.","required":false,"type":"string","in":"body"},{"name":"instructions","description":"System instructions, sent as the system message of every generation; `null` clears them.","required":false,"type":"string","in":"body"},{"name":"model","description":"Model identifier on the pinned AI provider; `null` falls back to the provider's default model. Cannot be combined with `model_route_id`.","required":false,"type":"string","in":"body"},{"name":"tool_bindings","description":"Tools attached to the agent — the only attachment field. Replaces the whole binding list; set to `null` to clear. See [Tool Bindings](/docs/modules/agents#tool-bindings).","required":false,"type":"array","in":"body"},{"name":"max_steps","description":"Maximum agent loop steps per turn; `null` restores the default of `20`.","required":false,"type":"integer","in":"body"},{"name":"tool_choice","description":"Tool choice strategy. Accepts a string (`\"auto\"`, `\"required\"`) or an object (`{ \"type\": \"tool\", \"tool_name\": \"my_tool\" }`). A forcing value (`\"required\"` or the object form) forbids a final assistant message on every step of every turn, including a resumed or continued one, so it requires a `has_tool_call` entry in `stop_conditions` — otherwise the write is refused with `FORCED_TOOL_CHOICE_CANNOT_STOP`.","required":false,"type":"unknown","in":"body"},{"name":"stop_conditions","description":"Conditions that end the agent's work early, on top of `max_steps`. Two scopes:\n\n`{\"type\": \"has_tool_call\", \"tool_name\": \"<resolved tool name>\"}` ends the **turn** after the step that calls the named tool. It narrows when the loop ends — it never lets it run past `max_steps`.\n\n`{\"type\": \"max_chain_generations\", \"max_generations\": <n>}` bounds the **continuation chain** instead: once the chain has spawned that many generations, further resumptions stop with `chain_limit` rather than extending it. It never shortens a turn. The effective ceiling is the smaller of this and the deployment's `MAX_CONTINUATION_CHAIN_GENERATIONS`, so an agent can be stricter than the platform but never looser.\n\nAn unknown `type`, a `has_tool_call` without a `tool_name`, a `max_chain_generations` whose `max_generations` is not a positive integer, or a non-object entry is rejected with 400.","required":false,"type":"array","in":"body"},{"name":"active_tool_ids","description":"Persisted tools from `tool_bindings` the model sees on every step. `null` or `[]` leaves every bound tool active. See [Active Tools](/docs/modules/agents#active-tools).","required":false,"type":"array","in":"body"},{"name":"guardrail_ids","description":"Guardrails attached at the agent scope.","required":false,"type":"array","in":"body"},{"name":"step_rules","description":"Per-step overrides of `tool_choice` and `active_tool_ids`, numbered from the first step of the turn; `null` clears them. See [Step Rules](/docs/modules/agents#step-rules).","required":false,"type":"array","in":"body"},{"name":"boundary_policy","description":"Restricts which SOAT actions the agent may invoke. Evaluated as the intersection with the caller's own policy, so it can only narrow.","required":false,"type":"object","in":"body"},{"name":"temperature","description":"Sampling temperature passed to the model; `null` restores the provider's default.","required":false,"type":"number","in":"body"},{"name":"knowledge_config","description":"Knowledge search run before every generation; its matches are prepended as reference context. `null` turns it off. See [Knowledge Config](/docs/modules/agents#knowledge-config).","required":false,"type":"object","in":"body"},{"name":"output_schema","description":"JSON Schema describing the structured object the model must return. When set, non-streaming generations constrain output to this schema and the parsed value is returned as `output.object`. The schema is enforced on the way back, not just sent to the model: an object that violates it fails the generation with 502 `OUTPUT_SCHEMA_VALIDATION_FAILED`, naming the violated field. Constraints beyond `required`/`type` (`minLength`, `enum`, `pattern`, `minItems`) are honored and are what reject a structurally valid but degenerate answer. See the Structured Output section in the Agents module docs.","required":false,"type":"object","in":"body"},{"name":"prompt_caching","description":"Prompt caching for this agent's turns. `{\"enabled\": true}` marks two cache breakpoints: the end of the system block, which covers the tool definitions and the instructions, and the last message of each step, which covers the conversation so far. A provider that caches by explicit breakpoint then reads, on every later step and every later turn of the session, the whole prefix the previous one sent, and writes only what was appended. Null or omitted is off: a cache write costs more than an uncached token, so an agent whose prefix is never re-read would pay for the privilege. A prefix below the model's minimum cacheable length is not cached. Cache reads are reported as `cached_tokens` and cache writes as `cache_write_tokens` on usage; a write with no `cache_write_tokens` price bills at the input rate.","required":false,"type":"object","in":"body"},{"name":"max_context_messages","description":"Maximum number of recent messages included in the context window. Null means no limit.","required":false,"type":"integer","in":"body"},{"name":"max_output_tokens","description":"Maximum number of tokens the model may generate per step of a turn. A response that reaches it stops with `stop_reason` `length`. Null leaves the model's default.","required":false,"type":"integer","in":"body"},{"name":"reasoning","description":"How much the model reasons before answering. Translated per provider (Anthropic thinking, Gemini thinking config, OpenAI reasoning effort) against what the model supports; a model that cannot reason ignores it. `none` turns reasoning off where the model allows it. Reasoning tokens are metered as output. Null leaves the model's default.","required":false,"type":"object","in":"body"},{"name":"single_session_per_actor","description":"When true, only one open session per actor_id is allowed for this agent.","required":false,"type":"boolean","in":"body"},{"name":"trace_content_mode","description":"Zero-retention opt-in for this agent. `null` inherits the project's setting; `none` means trace and generation content is never written. Setting `full` under a project whose own mode is `none` is refused with 400.","required":false,"type":"string","in":"body"},{"name":"on_approval_expiry","description":"What happens when one of this agent's held tool calls expires un-approved. `null` (the default) and `terminate` end the chain there — the expired approval, its `approvals.expired` event and the auto-filed `approval_expired` exception are the whole record. `react` spawns a continuation that reports the staleness to the agent, for an agent that acts on it.","required":false,"type":"string","in":"body"},{"name":"version_label","description":"Optional tag for the config version this write archives (e.g. `pre-tone-change`). Annotates the version only — it is not stored on the agent and is not part of the config, so labelling a change is never itself a change. Ignored when the write changes nothing, since no version is created.","required":false,"type":"string","in":"body"},{"name":"expected_version","description":"Refuses the write unless the resource is at this version.","required":false,"type":"integer","in":"body"}]},"patch-agent":{serviceClass:"Agents",operationId:"patchAgent",description:"Partially updates an existing agent. Identical to PUT — both perform partial updates.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"patch",pathParams:["agent_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"ai_provider_id","description":"Public ID of the AI provider to pin. Mutually exclusive with `model_route_id`; set to `null` when switching to a route.","required":false,"type":"string","in":"body"},{"name":"model_route_id","description":"Model route in the same project. Mutually exclusive with `ai_provider_id` and `model`; set to null to clear.","required":false,"type":"string","in":"body"},{"name":"name","description":"Display name; `null` clears it.","required":false,"type":"string","in":"body"},{"name":"instructions","description":"System instructions, sent as the system message of every generation; `null` clears them.","required":false,"type":"string","in":"body"},{"name":"model","description":"Model identifier on the pinned AI provider; `null` falls back to the provider's default model. Cannot be combined with `model_route_id`.","required":false,"type":"string","in":"body"},{"name":"tool_bindings","description":"Tools attached to the agent — the only attachment field. Replaces the whole binding list; set to `null` to clear. See [Tool Bindings](/docs/modules/agents#tool-bindings).","required":false,"type":"array","in":"body"},{"name":"max_steps","description":"Maximum agent loop steps per turn; `null` restores the default of `20`.","required":false,"type":"integer","in":"body"},{"name":"tool_choice","description":"Tool choice strategy. Accepts a string (`\"auto\"`, `\"required\"`) or an object (`{ \"type\": \"tool\", \"tool_name\": \"my_tool\" }`). A forcing value (`\"required\"` or the object form) forbids a final assistant message on every step of every turn, including a resumed or continued one, so it requires a `has_tool_call` entry in `stop_conditions` — otherwise the write is refused with `FORCED_TOOL_CHOICE_CANNOT_STOP`.","required":false,"type":"unknown","in":"body"},{"name":"stop_conditions","description":"Conditions that end the agent's work early, on top of `max_steps`. Two scopes:\n\n`{\"type\": \"has_tool_call\", \"tool_name\": \"<resolved tool name>\"}` ends the **turn** after the step that calls the named tool. It narrows when the loop ends — it never lets it run past `max_steps`.\n\n`{\"type\": \"max_chain_generations\", \"max_generations\": <n>}` bounds the **continuation chain** instead: once the chain has spawned that many generations, further resumptions stop with `chain_limit` rather than extending it. It never shortens a turn. The effective ceiling is the smaller of this and the deployment's `MAX_CONTINUATION_CHAIN_GENERATIONS`, so an agent can be stricter than the platform but never looser.\n\nAn unknown `type`, a `has_tool_call` without a `tool_name`, a `max_chain_generations` whose `max_generations` is not a positive integer, or a non-object entry is rejected with 400.","required":false,"type":"array","in":"body"},{"name":"active_tool_ids","description":"Persisted tools from `tool_bindings` the model sees on every step. `null` or `[]` leaves every bound tool active. See [Active Tools](/docs/modules/agents#active-tools).","required":false,"type":"array","in":"body"},{"name":"guardrail_ids","description":"Guardrails attached at the agent scope.","required":false,"type":"array","in":"body"},{"name":"step_rules","description":"Per-step overrides of `tool_choice` and `active_tool_ids`, numbered from the first step of the turn; `null` clears them. See [Step Rules](/docs/modules/agents#step-rules).","required":false,"type":"array","in":"body"},{"name":"boundary_policy","description":"Restricts which SOAT actions the agent may invoke. Evaluated as the intersection with the caller's own policy, so it can only narrow.","required":false,"type":"object","in":"body"},{"name":"temperature","description":"Sampling temperature passed to the model; `null` restores the provider's default.","required":false,"type":"number","in":"body"},{"name":"knowledge_config","description":"Knowledge search run before every generation; its matches are prepended as reference context. `null` turns it off. See [Knowledge Config](/docs/modules/agents#knowledge-config).","required":false,"type":"object","in":"body"},{"name":"output_schema","description":"JSON Schema describing the structured object the model must return. When set, non-streaming generations constrain output to this schema and the parsed value is returned as `output.object`. The schema is enforced on the way back, not just sent to the model: an object that violates it fails the generation with 502 `OUTPUT_SCHEMA_VALIDATION_FAILED`, naming the violated field. Constraints beyond `required`/`type` (`minLength`, `enum`, `pattern`, `minItems`) are honored and are what reject a structurally valid but degenerate answer. See the Structured Output section in the Agents module docs.","required":false,"type":"object","in":"body"},{"name":"prompt_caching","description":"Prompt caching for this agent's turns. `{\"enabled\": true}` marks two cache breakpoints: the end of the system block, which covers the tool definitions and the instructions, and the last message of each step, which covers the conversation so far. A provider that caches by explicit breakpoint then reads, on every later step and every later turn of the session, the whole prefix the previous one sent, and writes only what was appended. Null or omitted is off: a cache write costs more than an uncached token, so an agent whose prefix is never re-read would pay for the privilege. A prefix below the model's minimum cacheable length is not cached. Cache reads are reported as `cached_tokens` and cache writes as `cache_write_tokens` on usage; a write with no `cache_write_tokens` price bills at the input rate.","required":false,"type":"object","in":"body"},{"name":"max_context_messages","description":"Maximum number of recent messages included in the context window. Null means no limit.","required":false,"type":"integer","in":"body"},{"name":"max_output_tokens","description":"Maximum number of tokens the model may generate per step of a turn. A response that reaches it stops with `stop_reason` `length`. Null leaves the model's default.","required":false,"type":"integer","in":"body"},{"name":"reasoning","description":"How much the model reasons before answering. Translated per provider (Anthropic thinking, Gemini thinking config, OpenAI reasoning effort) against what the model supports; a model that cannot reason ignores it. `none` turns reasoning off where the model allows it. Reasoning tokens are metered as output. Null leaves the model's default.","required":false,"type":"object","in":"body"},{"name":"single_session_per_actor","description":"When true, only one open session per actor_id is allowed for this agent.","required":false,"type":"boolean","in":"body"},{"name":"trace_content_mode","description":"Zero-retention opt-in for this agent. `null` inherits the project's setting; `none` means trace and generation content is never written. Setting `full` under a project whose own mode is `none` is refused with 400.","required":false,"type":"string","in":"body"},{"name":"on_approval_expiry","description":"What happens when one of this agent's held tool calls expires un-approved. `null` (the default) and `terminate` end the chain there — the expired approval, its `approvals.expired` event and the auto-filed `approval_expired` exception are the whole record. `react` spawns a continuation that reports the staleness to the agent, for an agent that acts on it.","required":false,"type":"string","in":"body"},{"name":"version_label","description":"Optional tag for the config version this write archives (e.g. `pre-tone-change`). Annotates the version only — it is not stored on the agent and is not part of the config, so labelling a change is never itself a change. Ignored when the write changes nothing, since no version is created.","required":false,"type":"string","in":"body"},{"name":"expected_version","description":"Refuses the write unless the resource is at this version.","required":false,"type":"integer","in":"body"}]},"delete-agent":{serviceClass:"Agents",operationId:"deleteAgent",description:"Deletes an agent by ID. Fails with `409` if the agent has dependent generations or traces, or another project has accepted a share of it, unless `force=true` is passed, in which case those generations and traces are deleted along with the agent and the shares are revoked. Every share of the agent is revoked when it is deleted.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"delete",pathParams:["agent_id"],queryParams:["force"],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"force","description":"When `true`, deletes the agent's dependent generations and traces, revokes its accepted shares and leaves its ingestion rules naming it instead of returning `409 AGENT_HAS_DEPENDENTS`.\n","required":false,"type":"boolean","in":"query"}]},"create-agent-generation":{serviceClass:"Agents",operationId:"createAgentGeneration",description:"Sends messages to the agent, resolves its tools, and runs the AI model loop. Background by default: returns `202 Accepted` with a `generation_id` to poll via `GET /api/v1/generations/{generation_id}`. Pass `?wait=true` to block and receive the result inline, where client tools pause the generation and return `requires_action`. Streaming (`stream: true`) implies waiting. Pass `idempotency_key` to make a retry safe: a request whose key is already claimed runs nothing and answers `202` with the generation the key names. A credential scoped to a project the agent is shared with, through an accepted share, runs it in that project: on the publisher's configuration, recorded, metered and governed in the grantee project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"post",pathParams:["agent_id"],queryParams:["wait"],headerParams:["Idempotency-Key"],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"Idempotency-Key","description":"The request's deduplication key. Equivalent to `idempotency_key` in the request body; sending both with different values is `400 VALIDATION_FAILED`. A non-empty string of at most 255 characters.","required":false,"type":"string","in":"header"},{"name":"wait","description":"When omitted or `false` (default), the generation runs in the background and `202 Accepted` is returned immediately with a `generation_id` to poll. Pass `true` to block until the generation settles and receive the result. Mutually exclusive with `stream: true`. A tool call, `builtin` or MCP, always waits.","required":false,"type":"boolean","in":"query"},{"name":"messages","description":"Conversation turns, oldest first. Roles are `user` and `assistant`: the system prompt is the agent's `instructions`, so a `system` entry is refused with `400 SYSTEM_MESSAGE_NOT_ALLOWED`.","required":true,"type":"array","in":"body"},{"name":"stream","description":"When true the response is an SSE stream","required":false,"type":"boolean","in":"body"},{"name":"trace_id","description":"Optional trace ID to group generations. Each generation appends its own steps to the trace's steps object, and `step_count` covers them all.","required":false,"type":"string","in":"body"},{"name":"parent_trace_id","description":"The trace ID of the parent agent generation that triggered this one (for agent-to-agent calls)","required":false,"type":"string","in":"body"},{"name":"root_trace_id","description":"The trace ID of the root generation in the call chain; if omitted, this generation is the root","required":false,"type":"string","in":"body"},{"name":"max_call_depth","description":"Maximum nested agent-call depth; 0 short-circuits with a depth-guard response","required":false,"type":"integer","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as `X-Soat-Context-<key>` headers on every `http`, `mcp` and `builtin` tool call in this generation. The header name is the deployment's configured context prefix (`X-Soat-Context-` by default) plus the key verbatim — no character is re-cased. Keys are never case-converted — they round-trip exactly as sent. An invalid or colliding key is rejected with `400 INVALID_TOOL_CONTEXT_KEY`.","required":false,"type":"object","in":"body"},{"name":"action_id","description":"Logical action label recorded on the generation's usage meter, so spend can be rolled up per action (e.g. an A/B/C/D operating action).","required":false,"type":"string","in":"body"},{"name":"guardrail_context","description":"Caller-supplied guardrail context (the `context.*` namespace guard and class expressions read at tool-dispatch time). Free-form and never interpreted by the platform; a guardrail may combine it with a `context_tool` per its `context_mode`. See the guardrails module.","required":false,"type":"object","in":"body"},{"name":"metadata","description":"Caller-supplied key/value metadata attached to the generation record for per-run audit attribution (e.g. the ticket or case this action belongs to). Round-trips verbatim when the generation is fetched via the generations API. The bag is caller-owned and no key is reserved: server-owned state (usage attribution, the served agent version, the model route's record, the extraction summary, what knowledge retrieval served) lives in its own top-level generation fields and cannot be written from here. Use the request's own `action_id` field to set the usage-attribution label.","required":false,"type":"object","in":"body"},{"name":"idempotency_key","description":"Deduplication key, unique within the project, that makes a retry of an at-least-once delivery or an ambiguous failure safe. The first request under a key runs the generation; any later request carrying it runs nothing and answers `202` with the generation the key names, whatever state it has reached — including a retry that arrives while the original is still running. The key is claimed by the generation record for as long as the record exists.\n\nEvery other body field except `stream` is the request the key names: reusing a key with any of them changed, or on another agent of the project, is `409 IDEMPOTENCY_KEY_REUSED`. `stream` and `wait` say how the caller receives the generation, not what it is, so a retry may flip them.","required":false,"type":"string","in":"body"},{"name":"knowledge_config","description":"Per-generation knowledge retrieval override. Array filters (memory_store_ids, document_ids, document_paths) are unioned with the agent's stored knowledge_config; `tags` pairs are merged with the override winning per key; scalar fields (min_score, rrf_k, recency_half_life_days, limit) use the per-generation value when present.","required":false,"type":"object","in":"body"}]},"submit-agent-tool-outputs":{serviceClass:"Agents",operationId:"submitAgentToolOutputs",description:"Resumes a generation that was paused due to client tool calls. Provide tool outputs for each pending tool call.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"post",pathParams:["agent_id","generation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"generation_id","description":"Public ID of the generation paused at `requires_action`","required":true,"type":"string","in":"path"},{"name":"tool_outputs","description":"Results for the tool calls the paused generation is waiting on, one entry per `tool_call_id`.","required":true,"type":"array","in":"body"}]},"list-agent-versions":{serviceClass:"AgentVersions",operationId:"listAgentVersions",description:"Returns the agent's archived configurations, newest first. A version is written on create and on every subsequent write that changes the config — through the REST API or a formation apply alike. See [Versioning and Staged Rollout](/docs/modules/agents#versioning-and-staged-rollout).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"get",pathParams:["agent_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-agent-version":{serviceClass:"AgentVersions",operationId:"getAgentVersion",description:"Returns the exact configuration the agent held at a given version, so a generation can be traced back to the config that produced it.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"get",pathParams:["agent_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"version","description":"Archived config version number","required":true,"type":"integer","in":"path"}]},"restore-agent-version":{serviceClass:"AgentVersions",operationId:"restoreAgentVersion",description:"Copies the named version's configuration onto the agent as a **new** version rather than rewinding the counter, so history stays append-only and the versions in between remain retrievable. Restoring the config the agent already holds is a no-op and creates no version. The restored config fully replaces the current one: a field the archived version did not set is cleared, not merged. Restore re-validates the config, so a tool, provider, or guardrail deleted since the snapshot was taken fails the request instead of writing a broken agent.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"post",pathParams:["agent_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"version","description":"Archived config version number","required":true,"type":"integer","in":"path"},{"name":"label","description":"Tag for the version this restore creates. Defaults to `restored from v{version}`.","required":false,"type":"string","in":"body"}]},"set-agent-release":{serviceClass:"AgentVersions",operationId:"setAgentRelease",description:"Starts serving two archived versions side by side: `canary_percent` of traffic gets `canary_version`, the rest gets `stable_version`. Assignment is deterministic — it hashes the actor behind the request's session (falling back to the session itself), so one end user never flip-flops between configs mid-conversation. Requests with neither are split randomly. While a release is active the agent's live columns act as a **draft**: further edits archive new versions but do not disturb either side of the running split. End the rollout with `promote` or `abort`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"put",pathParams:["agent_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"stable_version","description":"An existing version to serve as the baseline","required":true,"type":"integer","in":"body"},{"name":"canary_version","description":"An existing version to trial. Must differ from `stable_version`.","required":true,"type":"integer","in":"body"},{"name":"canary_percent","description":"Percentage of traffic to assign to `canary_version`","required":true,"type":"integer","in":"body"},{"name":"promotion_gate","description":"Eval to gate promotion on. It must belong to this project and evaluate this agent; anything else is a `400`. Omit it, or send null, for a rollout that can be promoted at will.","required":false,"type":"string","in":"body"}]},"promote-agent-release":{serviceClass:"AgentVersions",operationId:"promoteAgentRelease",description:"Makes the canary version's config the agent's live config and clears the release. The canary is pinned by version, so an edit that landed mid-rollout is not promoted in its place — it stays an unreleased draft in the version history. When the release carries a `promotion_gate`, the eval it names must have a run that finished `completed` with `passed: true` **and** was pinned to the canary version (`agent_version`); otherwise the call is a `409` and the rollout is left running untouched. The run that cleared the gate is recorded as `eval_run_id` on the version that goes live.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"post",pathParams:["agent_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"}]},"abort-agent-release":{serviceClass:"AgentVersions",operationId:"abortAgentRelease",description:"Restores the stable version's config as the agent's live config and clears the release, so all traffic returns to the configuration the rollout was measured against — not to whatever draft the live columns happened to hold.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"post",pathParams:["agent_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"}]},"list-ai-providers":{serviceClass:"AIProviders",operationId:"listAiProviders",description:"Returns a list of AI provider configurations for a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ai-providers",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-ai-provider":{serviceClass:"AIProviders",operationId:"createAiProvider",description:"Creates a new LLM provider configuration. A `bedrock` or `vertex` record must carry a credential of its own — a linked `secret_id`, or an `apiKey` in `config`. Without one the provider SDK signs with the server's own credentials (the AWS default credential chain, Google Application Default Credentials), which is refused with `400 VALIDATION_FAILED` unless the deployment allows it.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ai-providers",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"name","description":"Provider configuration name","required":true,"type":"string","in":"body"},{"name":"provider","description":"LLM provider","required":true,"type":"string","in":"body"},{"name":"default_model","description":"Default model to use","required":true,"type":"string","in":"body"},{"name":"secret_id","description":"Secret ID containing API credentials. Required for `bedrock` and `vertex` unless `config.apiKey` carries one.","required":false,"type":"string","in":"body"},{"name":"base_url","description":"Custom base URL for the provider","required":false,"type":"string","in":"body"},{"name":"config","description":"Additional provider-specific configuration","required":false,"type":"object","in":"body"}]},"get-ai-provider":{serviceClass:"AIProviders",operationId:"getAiProvider",description:"Returns a specific AI provider configuration",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ai-providers",httpMethod:"get",pathParams:["ai_provider_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ai_provider_id","description":"AI Provider ID","required":true,"type":"string","in":"path"}]},"update-ai-provider":{serviceClass:"AIProviders",operationId:"updateAiProvider",description:"Updates an AI provider configuration",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ai-providers",httpMethod:"patch",pathParams:["ai_provider_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ai_provider_id","description":"AI Provider ID","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"provider","description":"LLM provider","required":false,"type":"string","in":"body"},{"name":"default_model","description":"","required":false,"type":"string","in":"body"},{"name":"secret_id","description":"","required":false,"type":"string","in":"body"},{"name":"base_url","description":"","required":false,"type":"string","in":"body"},{"name":"config","description":"","required":false,"type":"object","in":"body"}]},"delete-ai-provider":{serviceClass:"AIProviders",operationId:"deleteAiProvider",description:"Deletes an AI provider configuration. Live references — chats, agents, and model routes whose targets name this provider — always block deletion with `409 AI_PROVIDER_HAS_DEPENDENTS`; `force` does not override them, so delete or repoint those resources first. Soft dependents — price overrides and usage/generation records — also block with `409` unless `force=true`, which deletes the provider's price overrides and unlinks its usage history, preserving those rows: each usage event keeps the provider's id as its `ai_provider_id`. The `409` body's `error.meta` reports the counts, a sample of offending IDs, and a `forcible` flag that is `true` when a `force=true` retry would succeed.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ai-providers",httpMethod:"delete",pathParams:["ai_provider_id"],queryParams:["force"],headerParams:[],cookieParams:[],flags:[{"name":"ai_provider_id","description":"AI Provider ID","required":true,"type":"string","in":"path"},{"name":"force","description":"When `true`, delete the provider's price overrides and unlink its usage history so a provider with only soft dependents can be removed. Has no effect on live references (chats, agents, model routes), which always block deletion.\n","required":false,"type":"boolean","in":"query"}]},"list-ai-provider-models":{serviceClass:"AIProviders",operationId:"listAiProviderModels",description:"Asks the provider which models it can run, using this provider record's own credentials and configuration, and returns provider-native model ids — the same strings `default_model` and an agent's `model` carry. Which models are reachable is a property of the credential, not of the provider type: a Vertex provider sees only the publisher models its Google Cloud project and location serve, and a Bedrock provider only the foundation models enabled in its region. Reading the list is how a caller avoids pinning a model that fails at generation time. Not every provider type can answer. `azure` lists deployments an operator named rather than models, and `ollama` lists whatever was pulled onto that host, so both return `400 MODEL_LISTING_UNSUPPORTED`. Listing resolves credentials the same way generation does, so a record that can generate can list. The API-key types (`openai`, `groq`, `xai`, `gateway`, `custom`, `anthropic`, `google`) use the record's linked secret and cannot list without one. `bedrock` and `vertex` use the linked secret — IAM keys or a Bedrock API key, a Google service-account key. A record with no `secret_id` would fall back to the server's own credentials (the AWS default credential chain, Google Application Default Credentials); it can list only on a deployment that allows a record to use them, and returns `400 AI_PROVIDER_MISCONFIGURED` otherwise. A Vertex record needs no `config.project` when its secret is a service-account key, since the key file names its own project. A Vertex record in express mode (API key) cannot list at all: the publisher-model listing rejects API keys and needs a credential that asserts a principal, so it returns `400 MODEL_LISTING_UNSUPPORTED`. The Vertex answer is the publisher catalogue the record's `config.location` region serves. The project behind the credential is billed and quota'd for the call but does not filter the result, so a listed model may still be unavailable to that project at generation time. The provider answers its whole catalogue in one call; `q`, `limit` and `offset` narrow and page what it returned. A request without `limit` gets the first 50 models.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ai-providers",httpMethod:"get",pathParams:["ai_provider_id"],queryParams:["q","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"ai_provider_id","description":"AI Provider ID","required":true,"type":"string","in":"path"},{"name":"q","description":"Keeps the models whose `id` or `display_name` contains this text, ignoring case. Applied before paging, so `total` counts the matches.\n","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-ai-provider-prices":{serviceClass:"AIProviders",operationId:"getAiProviderPrices",description:"Returns the per-provider price overrides for this AI provider instance. An override prices this specific provider (e.g. an enterprise-negotiated rate or a gateway with markup) and wins over the global default at cost time. Authorized by the caller's access to the provider's project — so, unlike the global price book, a project's own overrides are visible here.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ai-providers",httpMethod:"get",pathParams:["ai_provider_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ai_provider_id","description":"AI Provider ID","required":true,"type":"string","in":"path"}]},"update-ai-provider-prices":{serviceClass:"AIProviders",operationId:"updateAiProviderPrices",description:"Upserts price overrides for this AI provider instance, keyed on (model, effective_from). The provider slug is taken from the AI provider itself, so only the model, rates, and effective_from are supplied. Authorized by the caller's access to the provider's project. `effective_from` must be in the future once the (model, component) has a price row — past prices are immutable, so corrections ship as new future-dated rows. A first price for a (model, component) nothing prices yet may be dated now or earlier, so a new provider is never live and unpriced.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ai-providers",httpMethod:"put",pathParams:["ai_provider_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ai_provider_id","description":"AI Provider ID","required":true,"type":"string","in":"path"},{"name":"prices","description":"","required":true,"type":"array","in":"body"}]},"list-api-keys":{serviceClass:"APIKeys",operationId:"listApiKeys",description:"Lists API keys accessible to the caller. - JWT admin: returns all API keys. - JWT regular user: returns only the user's own API keys. - Project-scoped credential (API key or OAuth token): returns the caller's own keys scoped to that project, or every key scoped to it when the credential holds `api-keys:ListApiKeys` on the project. Confinement to a project is not authority over it, and a key's metadata names its owner, prefix and attached policies.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/api-keys",httpMethod:"get",pathParams:[],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-api-key":{serviceClass:"APIKeys",operationId:"createApiKey",description:"Creates a new API key for the authenticated user. - `project_id` is optional. When set, the key is scoped to that single project. When omitted or null, the key is **unscoped** and spans every project its owner can reach. - If `policy_ids` is provided, the key's effective permissions are the intersection of the user's policies and the key's policies. - Otherwise the key inherits the user's permissions (confined to the key's project when scoped). - When the request is authenticated with a **project-scoped credential**, the new key is confined to that same project: omitting `project_id` defaults to it, naming a different project returns `403 API_KEY_PROJECT_SCOPE`, and `project_id: null` (an unscoped key) is likewise refused. Minting an unscoped key requires an unscoped credential.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/api-keys",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"name","description":"Key name for identification","required":true,"type":"string","in":"body"},{"name":"project_id","description":"Optional project ID to scope the key to. Omit or set null to create an unscoped key that spans projects.","required":false,"type":"string","in":"body"},{"name":"policy_ids","description":"Optional list of policy IDs to attach. Key permissions become the intersection of user policies and these policies.","required":false,"type":"array","in":"body"}]},"get-api-key":{serviceClass:"APIKeys",operationId:"getApiKey",description:"Returns details of an API key. Only the owner or an admin can access it, and a project-scoped credential can only reach keys in its own project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/api-keys",httpMethod:"get",pathParams:["api_key_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"api_key_id","description":"API key public ID (key_ prefix)","required":true,"type":"string","in":"path"}]},"update-api-key":{serviceClass:"APIKeys",operationId:"updateApiKey",description:"Updates an API key's name, project scope, or policies. The project scope can be changed to another project, set (scoping a previously unscoped key), or cleared with null (unscoping the key). Only the owner or an admin can update it. A project-scoped credential can only update keys in its own project, and cannot move a key to another project or unscope it.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/api-keys",httpMethod:"put",pathParams:["api_key_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"api_key_id","description":"API key public ID (key_ prefix)","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"project_id","description":"Re-scope the key to a different project, or set null to clear the scope (unscoped key). Omit to leave the scope unchanged.","required":false,"type":"string","in":"body"},{"name":"policy_ids","description":"Replace the key's policy list (empty array removes all)","required":false,"type":"array","in":"body"}]},"delete-api-key":{serviceClass:"APIKeys",operationId:"deleteApiKey",description:"Deletes an API key. Only the owner or an admin can delete it, and a project-scoped credential can only delete keys in its own project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/api-keys",httpMethod:"delete",pathParams:["api_key_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"api_key_id","description":"API key public ID (key_ prefix)","required":true,"type":"string","in":"path"}]},"list-approvals":{serviceClass:"Approvals",operationId:"listApprovals",description:"Returns approval items for a project, filterable by status, origin, and expiry.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/approvals",httpMethod:"get",pathParams:[],queryParams:["project_id","status","origin","expires_before","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"status","description":"Filter by lifecycle status","required":false,"type":"string","in":"query"},{"name":"origin","description":"Filter by producer origin","required":false,"type":"string","in":"query"},{"name":"expires_before","description":"Return only items expiring at or before this timestamp","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"list-approval-recurrences":{serviceClass:"Approvals",operationId:"listApprovalRecurrences",description:"Read-only rollup answering \"what keeps coming back?\" — groups items by `dedup_key` and returns those recurring at least `min_count` times, most-recurrent first. Each group carries the ordered item chain (via `previous_item_id`) and the resolution reasons in order, so a human can read recurring rejections side by side and graduate the pattern into a guardrail `deny`. Exact-key grouping only; no cluster state is stored.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/approvals",httpMethod:"get",pathParams:[],queryParams:["project_id","status","min_count","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"status","description":"Lifecycle status the groups are built from (default `rejected`)","required":false,"type":"string","in":"query"},{"name":"min_count","description":"Minimum items in a group for it to be returned","required":false,"type":"integer","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-approval":{serviceClass:"Approvals",operationId:"getApproval",description:"Returns a single approval item with its full evidence.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/approvals",httpMethod:"get",pathParams:["approval_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"approval_id","description":"Approval item ID","required":true,"type":"string","in":"path"}]},"approve-approval":{serviceClass:"Approvals",operationId:"approveApproval",description:"Approves the item. Optionally supply edited `arguments` to replace the proposed arguments (edit-then-approve); the original is preserved on the item. Expiry is re-checked at decision time — an expired item can never be approved.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/approvals",httpMethod:"post",pathParams:["approval_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"approval_id","description":"Approval item ID","required":true,"type":"string","in":"path"},{"name":"arguments","description":"Edited arguments to execute instead of the proposed ones. Editing composes a new call, so it additionally requires permission to make that call: `tools:CallTool` on the tool, and for a `builtin` proposal the action's own IAM action.","required":false,"type":"object","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as context headers on the approved action and on every tool call of the continuation turn, as on a generation's `tool_context`. Never stored on the item: supply it on the approve call itself, for a tool that authorizes with a credential minted at decision time. The server-pinned identity keys (`session_id`, `actor_id`, `actor_external_id`) are dropped. An invalid or colliding key is rejected with `400 INVALID_TOOL_CONTEXT_KEY` and nothing is resolved.","required":false,"type":"object","in":"body"}]},"reject-approval":{serviceClass:"Approvals",operationId:"rejectApproval",description:"Rejects the item. A reason is required and preserved on the item.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/approvals",httpMethod:"post",pathParams:["approval_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"approval_id","description":"Approval item ID","required":true,"type":"string","in":"path"},{"name":"reason","description":"Why the item is being rejected (required)","required":true,"type":"string","in":"body"}]},"list-audit-entries":{serviceClass:"AuditLog",operationId:"listAuditEntries",description:"Returns audit-log entries visible to the caller, newest first. All filters are optional and combine with AND. `resource_srn` is a prefix match (e.g. `srn:{project}:secret:` matches every secret action); every other filter is exact.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/audit-log",httpMethod:"get",pathParams:[],queryParams:["project_id","action","principal_id","resource_public_id","resource_srn","from","to","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (scopes results; required if not using project key auth for a specific project)","required":false,"type":"string","in":"query"},{"name":"action","description":"Exact permission-action string, e.g. `secrets:DeleteSecret`","required":false,"type":"string","in":"query"},{"name":"principal_id","description":"Public id of the principal (`user_…` or `key_…`)","required":false,"type":"string","in":"query"},{"name":"resource_public_id","description":"Exact target resource public id, e.g. `sec_…`","required":false,"type":"string","in":"query"},{"name":"resource_srn","description":"SRN prefix match, e.g. `srn:{project}:secret:`. The log is append-only, so a stored SRN is never rewritten; the filter matches it as stored.","required":false,"type":"string","in":"query"},{"name":"from","description":"Only entries created at or after this timestamp (ISO 8601)","required":false,"type":"string","in":"query"},{"name":"to","description":"Only entries created at or before this timestamp (ISO 8601)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above 200 it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"export-audit-entries":{serviceClass:"AuditLog",operationId:"exportAuditEntries",description:"Streams a project's audit-log entries as newline-delimited JSON — one entry object per line, oldest first — for archival before the retention window expires, or for shipping into an external system. `project_id` is required: the export is per-project by design. Filters behave exactly as they do on the list endpoint.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/audit-log",httpMethod:"get",pathParams:[],queryParams:["project_id","action","principal_id","resource_public_id","resource_srn","from","to"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project whose entries are exported","required":true,"type":"string","in":"query"},{"name":"action","description":"Exact permission-action string, e.g. `secrets:DeleteSecret`","required":false,"type":"string","in":"query"},{"name":"principal_id","description":"Public id of the principal (`user_…` or `key_…`)","required":false,"type":"string","in":"query"},{"name":"resource_public_id","description":"Exact target resource public id, e.g. `sec_…`","required":false,"type":"string","in":"query"},{"name":"resource_srn","description":"SRN prefix match, e.g. `srn:{project}:secret:`. The log is append-only, so a stored SRN is never rewritten; the filter matches it as stored.","required":false,"type":"string","in":"query"},{"name":"from","description":"Only entries created at or after this timestamp (ISO 8601)","required":false,"type":"string","in":"query"},{"name":"to","description":"Only entries created at or before this timestamp (ISO 8601)","required":false,"type":"string","in":"query"}]},"get-audit-entry":{serviceClass:"AuditLog",operationId:"getAuditEntry",description:"Returns a single audit-log entry, including its `detail` payload",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/audit-log",httpMethod:"get",pathParams:["entry_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"entry_id","description":"Audit entry ID","required":true,"type":"string","in":"path"}]},"list-chains":{serviceClass:"Chains",operationId:"listChains",description:"Returns the continuation chains in a project, newest first. Filter by `status` to find the chains that may still be spending (`active`) or the ones a budget stopped (`budget_exhausted`).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/chains",httpMethod:"get",pathParams:[],queryParams:["project_id","status","agent_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"status","description":"Filter by chain status","required":false,"type":"string","in":"query"},{"name":"agent_id","description":"Filter by the agent whose continuation opened the chain","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-chain":{serviceClass:"Chains",operationId:"getChain",description:"Returns a single continuation chain. To read the generations in it, list generations filtered by `chain_id`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/chains",httpMethod:"get",pathParams:["chain_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"chain_id","description":"Continuation chain ID","required":true,"type":"string","in":"path"}]},"list-chats":{serviceClass:"Chats",operationId:"listChats",description:"Returns all chats in the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/chats",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID to filter by","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-chat":{serviceClass:"Chats",operationId:"createChat",description:"Creates a new chat resource bound to an AI provider.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/chats",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ai_provider_id","description":"Public ID of the AI provider to pin. Optional: omit it to inherit the project's `default_model_route_id`, which gives the chat provider failover. Omitting it returns `400` when the project has no default, and cannot be combined with `model` — each route target names its own.","required":false,"type":"string","in":"body"},{"name":"project_id","description":"Public ID of the project. Required when the user belongs to multiple projects and no project key is used.\n","required":false,"type":"string","in":"body"},{"name":"name","description":"Optional human-readable name","required":false,"type":"string","in":"body"},{"name":"instructions","description":"Optional system message applied to all completions on this chat","required":false,"type":"string","in":"body"},{"name":"model","description":"Optional default model override","required":false,"type":"string","in":"body"}]},"get-chat":{serviceClass:"Chats",operationId:"getChat",description:"Returns a single chat by ID.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/chats",httpMethod:"get",pathParams:["chat_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"chat_id","description":"","required":true,"type":"string","in":"path"}]},"delete-chat":{serviceClass:"Chats",operationId:"deleteChat",description:"Deletes a chat by ID.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/chats",httpMethod:"delete",pathParams:["chat_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"chat_id","description":"","required":true,"type":"string","in":"path"}]},"create-chat-completion":{serviceClass:"Chats",operationId:"createChatCompletion",description:"OpenAI Chat Completions-compatible endpoint. Mirrors OpenAI's `POST /v1/chat/completions` path so an OpenAI SDK can target it by base URL alone. Names exactly one target. With `ai_provider_id` the completion is stateless: the provider's secret is decrypted and the appropriate Vercel AI SDK provider is called, with no server-side model fallback. With `chat_id` the stored chat supplies the provider (or the project's `default_model_route_id`), model and instructions. System content travels only in `instructions` — a `role: \"system\"` entry in `messages` is refused with `400 SYSTEM_MESSAGE_NOT_ALLOWED`. With `chat_id`, a request `instructions` replaces the chat's stored one for this call only; the stored value applies when the request carries none, and the two are never merged. Messages may use `document_id` instead of `content` with either target. Chats hold no message history — send the full `messages` array every time.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/chats",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ai_provider_id","description":"Public ID of the AI provider to run the completion against. Mutually exclusive with `chat_id`; exactly one of the two is required.\n","required":false,"type":"string","in":"body"},{"name":"chat_id","description":"Public ID of a stored chat supplying the provider, model and instructions. Mutually exclusive with `ai_provider_id`; exactly one of the two is required.\n","required":false,"type":"string","in":"body"},{"name":"model","description":"Model identifier. Overrides the provider's `default_model`, or the chat's `model`, when specified.\n","required":false,"type":"string","in":"body"},{"name":"instructions","description":"System instructions for this call. Sent to the provider as its `instructions` argument rather than as a message, which is the only place the AI SDK accepts system content (`allowSystemInMessages` defaults to false). This field is the only channel — a `role: \"system\"` entry in `messages` is refused with `400 SYSTEM_MESSAGE_NOT_ALLOWED`. With `chat_id`, this replaces the chat's stored `instructions` for this call only; the stored value applies when the request carries none, and the two are never merged.\n","required":false,"type":"string","in":"body"},{"name":"messages","description":"Ordered list of chat messages","required":true,"type":"array","in":"body"},{"name":"stream","description":"When `true` the response is an SSE stream of delta chunks. When `false` (default) a single JSON object is returned.\n","required":false,"type":"boolean","in":"body"}]},"list-conversations":{serviceClass:"Conversations",operationId:"listConversations",description:"Returns all conversations the caller has access to. If projectId is provided, returns only conversations in that project. project keys are scoped to a single project automatically.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"get",pathParams:[],queryParams:["project_id","actor_id","tags","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (optional)","required":false,"type":"string","in":"query"},{"name":"actor_id","description":"Filter by actor ID","required":false,"type":"string","in":"query"},{"name":"tags","description":"Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n","required":false,"type":"array","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-conversation":{serviceClass:"Conversations",operationId:"createConversation",description:"Creates a new conversation. project keys automatically infer the project from the key's scope; JWT callers must supply projectId.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID. Required for JWT auth; omit when using an project key.","required":false,"type":"string","in":"body"},{"name":"status","description":"Initial conversation status","required":false,"type":"string","in":"body"},{"name":"name","description":"Optional name for the conversation","required":false,"type":"string","in":"body"},{"name":"actor_id","description":"Actor ID to associate with this conversation","required":false,"type":"string","in":"body"},{"name":"retrieval","description":"Whether this conversation's turns are embedded for vector retrieval. Turns are stored and chunked either way, so `none` leaves them readable and reachable by full-text search without paying for an embedding. `null` (the default) inherits the project's `default_conversation_retrieval`.","required":false,"type":"string","in":"body"}]},"get-conversation":{serviceClass:"Conversations",operationId:"getConversation",description:"Returns a conversation by its ID",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"get",pathParams:["conversation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"}]},"update-conversation":{serviceClass:"Conversations",operationId:"updateConversation",description:"Updates the status of a conversation",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"patch",pathParams:["conversation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"},{"name":"status","description":"New conversation status","required":false,"type":"string","in":"body"},{"name":"name","description":"New conversation name","required":false,"type":"string","in":"body"},{"name":"retrieval","description":"Whether this conversation's turns are embedded for vector retrieval. Turns are stored and chunked either way, so `none` leaves them readable and reachable by full-text search without paying for an embedding. `null` (the default) inherits the project's `default_conversation_retrieval`. Switching it on embeds the turns already in the conversation, so the whole conversation becomes retrievable rather than only what is said next.","required":false,"type":"string","in":"body"}]},"delete-conversation":{serviceClass:"Conversations",operationId:"deleteConversation",description:"Deletes a conversation by its ID",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"delete",pathParams:["conversation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"}]},"list-conversation-messages":{serviceClass:"Conversations",operationId:"listConversationMessages",description:"Returns all messages (documents) attached to a conversation, ordered by position",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"get",pathParams:["conversation_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"add-conversation-message":{serviceClass:"Conversations",operationId:"addConversationMessage",description:"Creates a document from the message text and attaches it to the conversation at the given position. If position is omitted, it is appended at the end.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"post",pathParams:["conversation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"},{"name":"message","description":"Message text content to add to the conversation","required":true,"type":"string","in":"body"},{"name":"role","description":"Role of the message sender","required":true,"type":"string","in":"body"},{"name":"actor_id","description":"Optional actor ID to associate with this message (user identity)","required":false,"type":"string","in":"body"},{"name":"position","description":"Zero-based position. Defaults to MAX+1 (append).","required":false,"type":"integer","in":"body"},{"name":"metadata","description":"Caller-owned annotations on the message (e.g. a phone number, a channel), stored as sent and returned verbatim. The platform does not read the bag, so nothing in it reaches the model: a value the model should see belongs in `message`.","required":false,"type":"object","in":"body"}]},"generate-conversation-message":{serviceClass:"Conversations",operationId:"generateConversationMessage",description:"Generates the next message using the specified actor's linked agent or chat. Background by default: returns `202 Accepted` immediately and the reply lands as a new ConversationMessage when it completes — poll `GET /api/v1/conversations/{conversation_id}/messages` for it. Pass `?wait=true` to block and receive the result inline. On `completed`, the reply is persisted as a new ConversationMessage authored by that actor. On `requires_action`, nothing is persisted; the caller must submit tool outputs via the Agents module and re-invoke generate — so a flow using client tools should pass `?wait=true`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"post",pathParams:["conversation_id"],queryParams:["wait"],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"","required":true,"type":"string","in":"path"},{"name":"wait","description":"When omitted or `false` (default), the generation runs in the background and `202 Accepted` is returned immediately. Pass `true` to block until the generation settles and receive the result. A tool call, `builtin` or MCP, always waits.","required":false,"type":"boolean","in":"query"},{"name":"agent_id","description":"ID of the agent that will produce the next message.","required":true,"type":"string","in":"body"},{"name":"model","description":"Optional model override.","required":false,"type":"string","in":"body"},{"name":"stream","description":"If true, stream tokens via SSE. NOT IMPLEMENTED in v1 — returns 501.","required":false,"type":"boolean","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as `X-Soat-Context-<key>` headers on every `http`, `mcp` and `builtin` tool call in this generation. The header name is the deployment's configured context prefix (`X-Soat-Context-` by default) plus the key verbatim — no character is re-cased and keys are never case-converted, so they round-trip exactly as sent. An invalid or colliding key is rejected with `400 INVALID_TOOL_CONTEXT_KEY`.","required":false,"type":"object","in":"body"}]},"remove-conversation-message":{serviceClass:"Conversations",operationId:"removeConversationMessage",description:"Removes a document from a conversation",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"delete",pathParams:["conversation_id","document_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"},{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"}]},"get-conversation-tags":{serviceClass:"Conversations",operationId:"getConversationTags",description:"Returns all tags attached to the conversation",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"get",pathParams:["conversation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"}]},"replace-conversation-tags":{serviceClass:"Conversations",operationId:"replaceConversationTags",description:"Replaces all tags on the conversation with the provided tags",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"put",pathParams:["conversation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"merge-conversation-tags":{serviceClass:"Conversations",operationId:"mergeConversationTags",description:"Merges provided tags into the conversation's existing tags (existing tags are preserved unless overridden)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"patch",pathParams:["conversation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"list-deciders":{serviceClass:"Deciders",operationId:"listDeciders",description:"Returns the deciders defined in a project, newest first.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-decider":{serviceClass:"Deciders",operationId:"createDecider",description:"Creates a decider at version 1: a named question set and the backend that answers it, exactly one of `agent_id` and `tool_id`. Every question declares a finite answer space, validated here. The backend must be in the decider's project. An agent must carry no tool surface — no tool binding left active by `active_tool_ids` and no `knowledge_config.write_memory_store_id` — or the create is refused with `400 DECIDER_AGENT_NOT_TOOL_LESS`. A tool must be `http` or `pipeline` and must not pin `input` or `questions` in its `preset_parameters`, or the create is refused with `400 DECIDER_TOOL_NOT_CALLABLE`. A duplicate `name` in the project is `409 NAME_CONFLICT`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"name","description":"Human-readable name, unique per project","required":true,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"agent_id","description":"A tool-less agent that answers","required":false,"type":"string","in":"body"},{"name":"tool_id","description":"An http or pipeline tool that answers","required":false,"type":"string","in":"body"},{"name":"questions","description":"The questions, in the order the model is shown them.","required":true,"type":"array","in":"body"},{"name":"version_label","description":"Optional tag for version 1, e.g. `initial`","required":false,"type":"string","in":"body"}]},"get-decider":{serviceClass:"Deciders",operationId:"getDecider",description:"Returns a decider with its current question set and version.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"get",pathParams:["decider_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"decider_id","description":"The decider ID","required":true,"type":"string","in":"path"}]},"update-decider":{serviceClass:"Deciders",operationId:"updateDecider",description:"Updates any of `name`, `description`, the backend and `questions`. Naming `agent_id` or `tool_id` replaces the current backend; naming both is `400`. Only a change to `questions` archives a new version; a rename, a new backend or a rewrite of the questions the decider already holds leaves `version` where it is. A new backend is held to the same rules as on create.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"patch",pathParams:["decider_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"decider_id","description":"The decider ID","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"agent_id","description":"Replaces the backend with this agent","required":false,"type":"string","in":"body"},{"name":"tool_id","description":"Replaces the backend with this tool","required":false,"type":"string","in":"body"},{"name":"questions","description":"The questions, in the order the model is shown them.","required":false,"type":"array","in":"body"},{"name":"version_label","description":"Optional tag for the version this write archives. Ignored when the write changes no question, since no version is archived.","required":false,"type":"string","in":"body"},{"name":"expected_version","description":"Refuses the write unless the decider is at this version.","required":false,"type":"integer","in":"body"}]},"delete-decider":{serviceClass:"Deciders",operationId:"deleteDecider",description:"Deletes a decider and its version archive. Its decisions remain and keep naming the decider's ID.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"delete",pathParams:["decider_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"decider_id","description":"The decider ID","required":true,"type":"string","in":"path"}]},"list-decider-versions":{serviceClass:"Deciders",operationId:"listDeciderVersions",description:"Returns the decider's archived question sets, newest first. A version is written on create and on every write that changes `questions`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"get",pathParams:["decider_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"decider_id","description":"The decider ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-decider-version":{serviceClass:"Deciders",operationId:"getDeciderVersion",description:"Returns the question set a version held. A decision names the `decider_version` it was answered under, so this is how its criteria are read after the decider has changed.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"get",pathParams:["decider_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"decider_id","description":"The decider ID","required":true,"type":"string","in":"path"},{"name":"version","description":"The archived version number","required":true,"type":"integer","in":"path"}]},"restore-decider-version":{serviceClass:"Deciders",operationId:"restoreDeciderVersion",description:"Writes an archived question set back as the decider's live one, which archives it again as a new version rather than rewinding the counter. Restoring the question set the decider already holds is a no-op.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"post",pathParams:["decider_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"decider_id","description":"The decider ID","required":true,"type":"string","in":"path"},{"name":"version","description":"The archived version number","required":true,"type":"integer","in":"path"},{"name":"label","description":"Optional tag for the new version. Defaults to `restored from vN`.","required":false,"type":"string","in":"body"}]},"create-decision":{serviceClass:"Deciders",operationId:"createDecision",description:"Evaluates the decider against `input` and records the decision. The questions come from the decider, never from the request, so a call site can only supply what is judged. To send the questions with the request, use `POST /api/v1/decisions`. Everything that can refuse the request is checked before the decision is written, so a refusal is a `4xx` and never a polled failure: the agent's tool surface (`400 DECIDER_AGENT_NOT_TOOL_LESS`), a tool that cannot answer (`400 DECIDER_TOOL_NOT_CALLABLE`), a paused project (`409 PROJECT_PAUSED`) and, for an agent, quota admission (`429 QUOTA_EXCEEDED`). A tool backend is called with `{ input, questions }` and must answer `{ answers: [{ name, probability | choice | score, probabilities?, confidence? }] }`, the request and response of OpenAI's Decisions API; an answer outside that contract fails the decision with `DECISION_ANSWER_INVALID`. With `wait: false`, the default, the answer is `201` with the decision `queued`; poll `GET /api/v1/decisions/{decision_id}` or subscribe to `decisions.completed` and `decisions.failed`. With `wait: true` it is `201` with the decision settled.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"post",pathParams:["decider_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"decider_id","description":"The decider ID","required":true,"type":"string","in":"path"},{"name":"input","description":"What is judged, not stored: a string; a list of `user` messages whose `content` is a string or `input_text` and `input_image` parts (an image as a base64 data URL); or any other JSON value, which an agent is shown as indented JSON. An array of objects carrying `role` is read as messages.","required":true,"type":"unknown","in":"body"},{"name":"metadata","description":"Caller-owned annotations stored on the decision and returned on every read — typically the id of what was judged, since the input itself is not stored. Written once, with the decision. A non-object is rejected with `400 VALIDATION_FAILED` and no decision is written.","required":false,"type":"object","in":"body"},{"name":"wait","description":"True evaluates before answering and returns the settled decision. False — the default — returns the `queued` decision at once. A tool call, `builtin` or MCP, always waits.","required":false,"type":"boolean","in":"body"}]},"list-decisions":{serviceClass:"Deciders",operationId:"listDecisions",description:"Returns the decisions in a project, newest first.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"get",pathParams:[],queryParams:["project_id","decider_id","status","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"decider_id","description":"Only decisions requested from this decider","required":false,"type":"string","in":"query"},{"name":"status","description":"Only decisions in this status","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-inline-decision":{serviceClass:"Deciders",operationId:"createInlineDecision",description:"Evaluates `questions` sent with the request against `input`, answered by the agent or tool the request names (exactly one of `agent_id` and `tool_id`, held to the same rules as a decider's backend), and records the decision with its questions. The request and answers are those of OpenAI's Decisions API, with the backend in place of `model`. No decider names the questions, so the decision carries them in `questions` and has no `decider_id` or `decider_version`. Admission, `wait` and the tool contract are those of `POST /api/v1/deciders/{decider_id}/decisions`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"agent_id","description":"A tool-less agent that answers","required":false,"type":"string","in":"body"},{"name":"tool_id","description":"An http or pipeline tool that answers","required":false,"type":"string","in":"body"},{"name":"questions","description":"The questions, in the order the model is shown them.","required":true,"type":"array","in":"body"},{"name":"input","description":"What is judged, not stored: a string; a list of `user` messages whose `content` is a string or `input_text` and `input_image` parts (an image as a base64 data URL); or any other JSON value, which an agent is shown as indented JSON. An array of objects carrying `role` is read as messages.","required":true,"type":"unknown","in":"body"},{"name":"metadata","description":"Caller-owned annotations stored on the decision. A non-object is rejected with `400 VALIDATION_FAILED`.","required":false,"type":"object","in":"body"},{"name":"wait","description":"True evaluates before answering and returns the settled decision. False — the default — returns the `queued` decision at once. A tool call, `builtin` or MCP, always waits.","required":false,"type":"boolean","in":"body"}]},"get-decision":{serviceClass:"Deciders",operationId:"getDecision",description:"Returns a decision. Poll it after a `wait: false` request until `status` is `completed` or `failed`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"get",pathParams:["decision_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"decision_id","description":"The decision ID","required":true,"type":"string","in":"path"}]},"list-documents":{serviceClass:"Documents",operationId:"listDocuments",description:"Returns all documents the caller has access to. If projectId is provided, returns only documents in that project. project keys are scoped to a single project automatically. JWT users without projectId receive documents across all their accessible projects.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"get",pathParams:[],queryParams:["project_id","path_prefix","include_withdrawn","related_to","tags","metadata","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (optional)","required":false,"type":"string","in":"query"},{"name":"path_prefix","description":"Only documents filed under this directory. The prefix is a path boundary, not a substring: `/reports` returns `/reports/q1.txt` and never `/reports-archive/q1.txt`, and `/` selects the whole project. A leading slash is optional and a trailing one is ignored, so `reports`, `/reports` and `/reports/` are the same filter. `%` and `_` are literal characters, not wildcards.","required":false,"type":"string","in":"query"},{"name":"include_withdrawn","description":"Include withdrawn documents. A withdrawn document leaves every default read and is not in the knowledge index at all — its chunks are dropped when it is withdrawn — so this shows it in the listing but never in a search.","required":false,"type":"boolean","in":"query"},{"name":"related_to","description":"Only documents related to this one, on either side of the edge: what it points at, and what points at it. An id with no relations narrows the listing to nothing.","required":false,"type":"string","in":"query"},{"name":"tags","description":"Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n","required":false,"type":"array","in":"query"},{"name":"metadata","description":"A `MetadataFilter` as JSON, url-encoded. It travels as JSON rather than as `key:value` pairs because the match is exact and a query string cannot otherwise say whether `3` is the number or the string.","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-document":{serviceClass:"Documents",operationId:"createDocument",description:"Creates a new text document and generates an embedding vector for semantic search. project keys automatically infer the project from the key's scope; JWT callers must supply projectId.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID. Required for JWT auth; omit when using an project key.","required":false,"type":"string","in":"body"},{"name":"content","description":"","required":true,"type":"string","in":"body"},{"name":"path","description":"Logical path within the project (e.g. /reports/q1.txt). Defaults to `/<filename>`, or to `/<document_id>.txt` when neither is given — a document with no path is reachable only by its id, since a prefix filter never matches null.","required":false,"type":"string","in":"body"},{"name":"filename","description":"","required":false,"type":"string","in":"body"},{"name":"title","description":"Document title","required":false,"type":"string","in":"body"},{"name":"metadata","description":"Arbitrary metadata object. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.","required":false,"type":"object","in":"body"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":false,"type":"object","in":"body"},{"name":"chunk_strategy","description":"How to split the content into embeddable chunks. `whole` (default) stores the content as a single chunk; `size` splits into fixed-size character windows with overlap. `page` is equivalent to `whole` for plain text.","required":false,"type":"string","in":"body"},{"name":"chunk_size","description":"Window size in characters when `chunk_strategy=size`. Defaults to 1000.","required":false,"type":"integer","in":"body"},{"name":"chunk_overlap","description":"Overlap in characters between consecutive windows when `chunk_strategy=size`. Defaults to 200.","required":false,"type":"integer","in":"body"}]},"ingest-document":{serviceClass:"Documents",operationId:"ingestDocument",description:"Parses an already-uploaded file and creates one Document split into one or more embedded chunks. The source format is detected from the file's content type: PDFs are parsed page-by-page; `text/plain` and `text/markdown` files are read as a single source. How the source is chunked is controlled by `chunk_strategy`. A file can only back one Document — a second call with the same `file_id` returns `409 FILE_ALREADY_INGESTED`. To re-process an already-ingested file (e.g. with a different `chunk_strategy`), use `POST /documents/{document_id}/ingest`; to ingest the same source under a different path, upload a new copy of the file first.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"post",pathParams:[],queryParams:["wait"],headerParams:[],cookieParams:[],flags:[{"name":"wait","description":"When omitted or `false` (default), processing runs in the background and `202 Accepted` is returned immediately with `status=pending`. Pass `true` to block until processing completes and receive `201 Created` with `status=ready`.","required":false,"type":"boolean","in":"query"},{"name":"file_id","description":"ID of the uploaded file. Must be one of application/pdf, text/plain, text/markdown.","required":true,"type":"string","in":"body"},{"name":"project_id","description":"Project ID. Required for JWT auth; omit when using a project key.","required":false,"type":"string","in":"body"},{"name":"path_prefix","description":"Path prefix under which to store the document (e.g. /docs/). The filename is appended automatically.","required":false,"type":"string","in":"body"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":false,"type":"object","in":"body"},{"name":"chunk_strategy","description":"How to split the source into chunks. `page` (default) creates one chunk per non-empty page (PDF); for non-paged sources it yields a single chunk. `whole` joins everything into one chunk. `size` splits into fixed-size character windows with overlap.","required":false,"type":"string","in":"body"},{"name":"chunk_size","description":"Window size in characters when `chunk_strategy=size`. Defaults to 1000.","required":false,"type":"integer","in":"body"},{"name":"chunk_overlap","description":"Overlap in characters between consecutive windows when `chunk_strategy=size`. Defaults to 200.","required":false,"type":"integer","in":"body"}]},"export-documents":{serviceClass:"Documents",operationId:"exportDocuments",description:"Streams a project's documents as newline-delimited JSON — one document object per line, oldest first — for archiving the corpus or shipping it into another system. `project_id` is required: the export is per-project by design. The rows are the rows the listing returns for the same caller, so a policy that hides a document hides it here too, and withdrawn documents and the reserved `/.system/` root are left out.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"get",pathParams:[],queryParams:["project_id","path_prefix"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project whose documents are exported","required":true,"type":"string","in":"query"},{"name":"path_prefix","description":"Only documents filed under this directory. The prefix is a path boundary, not a substring, exactly as on the listing.","required":false,"type":"string","in":"query"}]},"get-document":{serviceClass:"Documents",operationId:"getDocument",description:"Returns a document with its text content",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"get",pathParams:["document_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"}]},"update-document":{serviceClass:"Documents",operationId:"updateDocument",description:"Updates document content, title, path, metadata, or tags. Supplying `path` moves the document to a new logical path within the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"patch",pathParams:["document_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"content","description":"New text content","required":false,"type":"string","in":"body"},{"name":"title","description":"New title","required":false,"type":"string","in":"body"},{"name":"path","description":"Logical path within the project (e.g. /reports/q1.txt). Pass null to clear.","required":false,"type":"string","in":"body"},{"name":"metadata","description":"Arbitrary metadata object, replacing the stored bag; `null` clears it. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.","required":false,"type":"object","in":"body"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":false,"type":"object","in":"body"},{"name":"expected_version","description":"Refuses the write unless the document is at this version.","required":false,"type":"integer","in":"body"}]},"delete-document":{serviceClass:"Documents",operationId:"deleteDocument",description:"Deletes a document and its underlying file",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"delete",pathParams:["document_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"}]},"list-document-relations":{serviceClass:"Documents",operationId:"listDocumentRelations",description:"Returns the typed edges this document asserts about others, oldest first. An edge is owned by the document it leaves, so this is what the document claims — use `?related_to=` on the listing to find what claims something about it.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"get",pathParams:["document_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-document-relation":{serviceClass:"Documents",operationId:"createDocumentRelation",description:"Asserts a typed edge from this document to another in the same project. Asserting the same edge twice is `409`: an edge is a fact, and the second assertion is the same fact. Writing an edge is a write of the asserting document (`documents:UpdateDocument`); the document it points at is unchanged, which is what lets an agent record what its own report derives from without being able to make another report claim anything.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"post",pathParams:["document_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"","required":true,"type":"string","in":"path"},{"name":"type","description":"What this document claims about the other","required":true,"type":"string","in":"body"},{"name":"to_document_id","description":"The document the edge points at. Must be in the same project, and visible to the caller.","required":true,"type":"string","in":"body"}]},"delete-document-relation":{serviceClass:"Documents",operationId:"deleteDocumentRelation",description:"Removes an edge this document asserts. Both documents are left as they are — retracting a claim is not a change to what it was about.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"delete",pathParams:["document_id","relation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"","required":true,"type":"string","in":"path"},{"name":"relation_id","description":"","required":true,"type":"string","in":"path"}]},"get-document-status":{serviceClass:"Documents",operationId:"getDocumentStatus",description:"Returns a lightweight ingestion status payload for polling — `status`, `chunk_count`, `total_pages`, and (when failed) `error`. Unlike `GET /documents/{document_id}`, it never returns the assembled chunk content, so it is cheap to poll on large documents. A document whose ingestion has stalled (no progress past the configured timeout) is transitioned to `failed` with `error=INGESTION_TIMEOUT` on read.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"get",pathParams:["document_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"}]},"withdraw-document":{serviceClass:"Documents",operationId:"withdrawDocument",description:"Takes a document out of every default read while keeping its history. The withdrawal is archived as a **tombstone version**, so one mechanism answers what a document holds now and there is no second lifecycle flag for a reader to miss. Its chunks are dropped from the knowledge index, so a withdrawn document costs a live search nothing. The content is not lost with them: it is in the version before the tombstone, which [`POST /api/v1/documents/{document_id}/versions/{version}/restore`](/docs/api/documents/restore-document-version) re-chunks from. `DELETE` stays what it is — permanent, and it removes the backing file. Withdrawal is the reversible act. It does not apply under `/.system/`: a platform-written document keeps the owning module's lifecycle.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"post",pathParams:["document_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"document_id","description":"","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"version_label","description":"Optional tag for the tombstone version this write archives.","required":false,"type":"string","in":"body"},{"name":"expected_version","description":"Refuses the withdrawal unless the document is at this version.","required":false,"type":"integer","in":"body"}]},"list-document-versions":{serviceClass:"Documents",operationId:"listDocumentVersions",description:"Returns the document's archived states, newest first. A version is written on create and on every subsequent write that changes the content or its annotations, and a withdrawal is archived as a version carrying no content.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"get",pathParams:["document_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-document-version":{serviceClass:"Documents",operationId:"getDocumentVersion",description:"Returns the exact content and annotations the document held at a given version, which is what lets a run that cited a version read what it read.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"get",pathParams:["document_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"","required":true,"type":"string","in":"path"},{"name":"version","description":"","required":true,"type":"integer","in":"path"}]},"restore-document-version":{serviceClass:"Documents",operationId:"restoreDocumentVersion",description:"Writes an archived version's content and annotations back as the document's live state, which archives them again as a **new** version rather than rewinding the counter — so a run citing any version in between still resolves. The restore runs through the ordinary update path, so the content is re-chunked and re-embedded; restoring the state the document already holds is a no-op and archives nothing. Restoring any content version of a withdrawn document brings it back into listings and search. A tombstone version has no content, so naming one is `400 VALIDATION_FAILED`: restore the version before it instead.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"post",pathParams:["document_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"","required":true,"type":"string","in":"path"},{"name":"version","description":"","required":true,"type":"integer","in":"path"},{"name":"label","description":"Optional tag for the version this restore archives. Defaults to `restored from v<version>`.","required":false,"type":"string","in":"body"}]},"reingest-document":{serviceClass:"Documents",operationId:"reingestDocument",description:"Re-runs ingestion for an existing document against its already-stored source file. Existing chunks are discarded and the document is reset to `status=pending` before re-processing. Use this to recover a document stuck in `processing`/`failed` or to re-chunk with a different strategy without re-uploading the file. Background by default (`202`); pass `?wait=true` to run synchronously (`201`).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"post",pathParams:["document_id"],queryParams:["wait"],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"},{"name":"wait","description":"When omitted or `false` (default), processing runs in the background and `202 Accepted` is returned immediately with `status=pending`. Pass `true` to block until processing completes and receive `201 Created` with `status=ready`.","required":false,"type":"boolean","in":"query"},{"name":"chunk_strategy","description":"How to split the source into chunks. Defaults to `page`.","required":false,"type":"string","in":"body"},{"name":"chunk_size","description":"Window size in characters when `chunk_strategy=size`. Defaults to 1000.","required":false,"type":"integer","in":"body"},{"name":"chunk_overlap","description":"Overlap in characters between consecutive windows when `chunk_strategy=size`. Defaults to 200.","required":false,"type":"integer","in":"body"}]},"complete-ingestion-callback":{serviceClass:"Documents",operationId:"completeIngestionCallback",description:"Token-authed callback for a tool converter that deferred conversion by returning `{ \"status\": \"pending\" }` (see the Ingestion Rules module docs). Not IAM-gated — the external converter is not a SOAT principal, so it authenticates with the single-use token minted for this document and ingestion attempt (delivered as `callback.token` / embedded in `callback.url` in the original converter invocation). Accepted only while the document is still awaiting that exact attempt; rejected with `409` if the attempt already completed, timed out, or was superseded by a re-ingest.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"post",pathParams:["document_id"],queryParams:["token"],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"},{"name":"token","description":"Single-use signed token from the original `callback.token`","required":true,"type":"string","in":"query"},{"name":"text","description":"","required":false,"type":"string","in":"body"},{"name":"pages","description":"","required":false,"type":"array","in":"body"}]},"get-document-tags":{serviceClass:"Documents",operationId:"getDocumentTags",description:"Returns all tags attached to the document",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"get",pathParams:["document_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"}]},"replace-document-tags":{serviceClass:"Documents",operationId:"replaceDocumentTags",description:"Replaces all tags on the document with the provided tags (not merged)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"put",pathParams:["document_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"merge-document-tags":{serviceClass:"Documents",operationId:"mergeDocumentTags",description:"Merges provided tags into the document's existing tags (existing tags are preserved unless overridden)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"patch",pathParams:["document_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"create-embeddings":{serviceClass:"Embeddings",operationId:"createEmbeddings",description:"Generates embedding vectors for one or more text inputs using the server's configured embedding model. Provide `input` for a single text or `inputs` for a batch. At least one is required. Returns `embedding` when `input` is used, and `embeddings` when `inputs` is used. `project_id` names the project the call's token usage is metered against, so embedding spend reaches the usage rollup and the project's `cost_usd` / `tokens` quotas. A project-scoped credential supplies its own project; a call that names none and is bound to none is served but not metered.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/embeddings",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project the call's usage is billed to. Optional; a project-scoped credential supplies its own. Requires write access to the named project.","required":false,"type":"string","in":"body"},{"name":"input","description":"Single text to embed.","required":false,"type":"string","in":"body"},{"name":"inputs","description":"Batch of texts to embed. At most 256 per request — each input is one call to the embedding model, so a larger batch is refused (`VALIDATION_FAILED`) rather than queued. Split it across requests.","required":false,"type":"array","in":"body"}]},"list-datasets":{serviceClass:"Evaluations",operationId:"listDatasets",description:"Returns the datasets defined in a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-dataset":{serviceClass:"Evaluations",operationId:"createDataset",description:"Creates a project-scoped dataset — a named collection of test cases an eval runs an agent against. Names are unique per project. Datasets are operator-owned **fixtures**. The platform's content purge never deletes or mutates a dataset item, so erasing a generation cannot silently stop a test suite from being runnable.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"name","description":"Unique name within the project","required":true,"type":"string","in":"body"},{"name":"description","description":"What this suite covers","required":false,"type":"string","in":"body"}]},"get-dataset":{serviceClass:"Evaluations",operationId:"getDataset",description:"Returns a specific dataset",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"get",pathParams:["dataset_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"dataset_id","description":"Dataset ID","required":true,"type":"string","in":"path"}]},"update-dataset":{serviceClass:"Evaluations",operationId:"updateDataset",description:"Updates a dataset's name and/or description",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"put",pathParams:["dataset_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"dataset_id","description":"Dataset ID","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"}]},"delete-dataset":{serviceClass:"Evaluations",operationId:"deleteDataset",description:"Deletes a dataset, its items, and every eval bound to it. Results of runs that already scored those items keep their frozen copies of the input and expected output.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"delete",pathParams:["dataset_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"dataset_id","description":"Dataset ID","required":true,"type":"string","in":"path"}]},"list-dataset-items":{serviceClass:"Evaluations",operationId:"listDatasetItems",description:"Returns the test cases in a dataset, oldest first",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"get",pathParams:["dataset_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"dataset_id","description":"Dataset ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-dataset-item":{serviceClass:"Evaluations",operationId:"createDatasetItem",description:"Adds one test case. `input` is replayed verbatim as the generation's messages, so it must be a non-empty array of `{ role, content }`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"post",pathParams:["dataset_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"dataset_id","description":"Dataset ID","required":true,"type":"string","in":"path"},{"name":"input","description":"Messages replayed verbatim as the generation's input","required":true,"type":"array","in":"body"},{"name":"expected_output","description":"Reference answer for exact_match / embedding_similarity / llm_judge scorers","required":false,"type":"string","in":"body"},{"name":"metadata","description":"Free-form tags, opaque to the platform","required":false,"type":"object","in":"body"}]},"create-dataset-item-from-generation":{serviceClass:"Evaluations",operationId:"createDatasetItemFromGeneration",description:"Promotes a real, completed generation into a test case: its input messages become the item's `input`, and its own answer becomes `expected_output` unless you supply one. Use it to build an evaluation set out of production traffic rather than hand-authoring fixtures. The item is a **copy**, not a view. It keeps working after the source generation's content is purged, and `source_generation_id` goes null if that generation is deleted — a purge can never quietly stop a suite from being runnable. Requires both `evaluations:CreateDataset` and `generations:GetGeneration`: the call copies content out of a generation, so a principal that may not read that generation may not curate it either. Only a **completed** generation can be promoted (`409 GENERATION_NOT_COMPLETED`), and only while its content is still available: an agent or project running with `trace_content_mode: none` never stored the input, and a purged or expired generation no longer has it (`409 GENERATION_CONTENT_UNAVAILABLE`). Generations that predate input recording answer the same way.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"post",pathParams:["dataset_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"dataset_id","description":"Dataset ID","required":true,"type":"string","in":"path"},{"name":"generation_id","description":"The completed generation to promote. Must belong to the same project as the dataset.","required":true,"type":"string","in":"body"},{"name":"expected_output","description":"Reference answer. Omit to use the generation's own answer; pass `null` to store the item with no reference answer.","required":false,"type":"string","in":"body"},{"name":"metadata","description":"Free-form tags, opaque to the platform","required":false,"type":"object","in":"body"}]},"update-dataset-item":{serviceClass:"Evaluations",operationId:"updateDatasetItem",description:"Updates a test case. Runs that already scored it are unaffected — each result carries its own frozen copy of the input and expected output.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"put",pathParams:["dataset_id","item_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"dataset_id","description":"Dataset ID","required":true,"type":"string","in":"path"},{"name":"item_id","description":"Dataset item ID","required":true,"type":"string","in":"path"},{"name":"input","description":"Messages replayed verbatim as the generation's input","required":false,"type":"array","in":"body"},{"name":"expected_output","description":"","required":false,"type":"string","in":"body"},{"name":"metadata","description":"","required":false,"type":"object","in":"body"}]},"delete-dataset-item":{serviceClass:"Evaluations",operationId:"deleteDatasetItem",description:"Deletes a test case. Results of runs that already scored it stay readable; their `dataset_item_id` becomes null.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"delete",pathParams:["dataset_id","item_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"dataset_id","description":"Dataset ID","required":true,"type":"string","in":"path"},{"name":"item_id","description":"Dataset item ID","required":true,"type":"string","in":"path"}]},"list-evals":{serviceClass:"Evaluations",operationId:"listEvals",description:"Returns the evals defined in a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-eval":{serviceClass:"Evaluations",operationId:"createEval",description:"Binds an agent under test to a dataset and a list of scorers. The agent and the dataset must belong to the same project as the eval; a cross-project reference is rejected with 400. Scorer config is frozen here rather than read from the agent at run time, so two runs of the same eval are always judged by the same criteria and their comparison measures the agent instead of the config drifting underneath it. Each scorer `type` may appear at most once.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"name","description":"Unique name within the project","required":true,"type":"string","in":"body"},{"name":"agent_id","description":"The agent under test","required":true,"type":"string","in":"body"},{"name":"dataset_id","description":"The dataset to run it against","required":true,"type":"string","in":"body"},{"name":"scorers","description":"Scorer configs, a discriminated union on `type`. Each type may appear at most once. Every scorer produces `{ score: 0–1, passed: boolean }`; binary scorers emit 0 or 1.\n\n`exact_match` compares the trimmed output text to `expected_output`. `contains` looks for `value` in the output text. `json_logic` evaluates `expression` over `{ input, output, object, expected, item.metadata }`, where `object` is the structured output (absent when the agent has no `output_schema`). `output_schema` validates the structured output against the scorer's own `schema`, falling back to the agent's; it requires the agent to carry an `output_schema`, because without one the platform emits no structured output and every item would score 0.\n\n`llm_judge` grades the output with a model completion, returning a continuous score plus its `reasoning`. Its `pass_threshold` is required: a continuous score says nothing about where \"good enough\" is, and a defaulted cutoff would silently decide the gate.\n\n`embedding_similarity` embeds the output text and `expected_output` with the platform's configured embedding model (`EMBEDDING_PROVIDER` / `EMBEDDING_MODEL` — the same stack document ingestion uses) and scores their cosine similarity, clamped to 0-1. Its `pass_threshold` is required for the same reason as the judge's. An item without an `expected_output` scores 0; an embedding backend failure marks the **item** errored, never a score of 0.\n\n`tool` runs a custom scoring algorithm: a server-callable project tool the engine invokes once per item with the item's context. Unlike the built-in types it may appear several times, each under a distinct `name` — outcomes and aggregates key on the name.\n\n`decider` grades each item with a decision of a project decider: its `score` expression reads the decision's `answers`. Like `tool`, it keys on its `name` and may appear several times.","required":true,"type":"array","in":"body"},{"name":"pass_threshold","description":"0–1. The run passes iff its pass rate — passed items over non-errored items — is at least this. Null reports scores without gating on them.","required":false,"type":"number","in":"body"},{"name":"group_by","description":"A key of the items' `metadata`. A run rolls its scores up per string value of that key in `aggregate_scores.grouping`. Null reports no grouping.","required":false,"type":"string","in":"body"}]},"get-eval":{serviceClass:"Evaluations",operationId:"getEval",description:"Returns a specific eval",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"get",pathParams:["eval_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"eval_id","description":"Eval ID","required":true,"type":"string","in":"path"}]},"update-eval":{serviceClass:"Evaluations",operationId:"updateEval",description:"Updates an eval. Changing `agent_id` re-validates the scorers against the new agent, since an `output_schema` scorer that was legal against the old one may not be.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"put",pathParams:["eval_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"eval_id","description":"Eval ID","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"agent_id","description":"","required":false,"type":"string","in":"body"},{"name":"dataset_id","description":"","required":false,"type":"string","in":"body"},{"name":"scorers","description":"Scorer configs, a discriminated union on `type`. Each type may appear at most once. Every scorer produces `{ score: 0–1, passed: boolean }`; binary scorers emit 0 or 1.\n\n`exact_match` compares the trimmed output text to `expected_output`. `contains` looks for `value` in the output text. `json_logic` evaluates `expression` over `{ input, output, object, expected, item.metadata }`, where `object` is the structured output (absent when the agent has no `output_schema`). `output_schema` validates the structured output against the scorer's own `schema`, falling back to the agent's; it requires the agent to carry an `output_schema`, because without one the platform emits no structured output and every item would score 0.\n\n`llm_judge` grades the output with a model completion, returning a continuous score plus its `reasoning`. Its `pass_threshold` is required: a continuous score says nothing about where \"good enough\" is, and a defaulted cutoff would silently decide the gate.\n\n`embedding_similarity` embeds the output text and `expected_output` with the platform's configured embedding model (`EMBEDDING_PROVIDER` / `EMBEDDING_MODEL` — the same stack document ingestion uses) and scores their cosine similarity, clamped to 0-1. Its `pass_threshold` is required for the same reason as the judge's. An item without an `expected_output` scores 0; an embedding backend failure marks the **item** errored, never a score of 0.\n\n`tool` runs a custom scoring algorithm: a server-callable project tool the engine invokes once per item with the item's context. Unlike the built-in types it may appear several times, each under a distinct `name` — outcomes and aggregates key on the name.\n\n`decider` grades each item with a decision of a project decider: its `score` expression reads the decision's `answers`. Like `tool`, it keys on its `name` and may appear several times.","required":false,"type":"array","in":"body"},{"name":"pass_threshold","description":"","required":false,"type":"number","in":"body"},{"name":"group_by","description":"A key of the items' `metadata` to group run scores by; null clears it, omitting it leaves it unchanged.","required":false,"type":"string","in":"body"}]},"delete-eval":{serviceClass:"Evaluations",operationId:"deleteEval",description:"Deletes an eval, its runs, and their results",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"delete",pathParams:["eval_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"eval_id","description":"Eval ID","required":true,"type":"string","in":"path"}]},"list-eval-runs":{serviceClass:"Evaluations",operationId:"listEvalRuns",description:"Returns an eval's runs, newest first",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"get",pathParams:["eval_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"eval_id","description":"Eval ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"start-eval-run":{serviceClass:"Evaluations",operationId:"startEvalRun",description:"Runs the eval against its dataset, creating one real agent generation per item and scoring the outputs. `wait: true` executes the run synchronously and returns it terminal, with its scores. The dataset is capped at 25 items for a synchronous run; a larger one is rejected with 400 rather than partially scored. `wait: false` (the default) enqueues one task per item and returns immediately with `status: \"queued\"`. A worker executes the items and the run settles itself; poll `GET /evals/{eval_id}/runs/{eval_run_id}` for the terminal status, or subscribe to the `eval_run.completed` webhook. There is no item cap on a queued run. The whole run is pinned to **one** agent version, stamped on `agent_version`: pass one explicitly to evaluate a canary before promoting it, or omit it to use the active release's stable version (or the live draft when no release is in effect). Without the pin, release assignment would bucket each item independently and blend two configs into a single score. With `baseline_run_id`, the finished run's `aggregate_scores.baseline` carries per-scorer deltas against that run, computed over the items present and scorable in **both** runs, with the divergence counted. A delta over a shifted dataset is therefore never presented as a clean comparison.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"post",pathParams:["eval_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"eval_id","description":"Eval ID","required":true,"type":"string","in":"path"},{"name":"wait","description":"True runs the eval synchronously (25-item cap) and returns a terminal run with its scores. False — the default — enqueues the items and returns a `queued` run immediately.","required":false,"type":"boolean","in":"body"},{"name":"agent_version","description":"An archived agent version to evaluate. Defaults to the active release's stable version, or the live draft version when no release is in effect.","required":false,"type":"integer","in":"body"},{"name":"baseline_run_id","description":"A terminal run of the same eval to compare against. The finished run's `aggregate_scores.baseline` reports per-scorer deltas over the item intersection. A run of a different eval is rejected with 400.","required":false,"type":"string","in":"body"},{"name":"metadata","description":"Caller-supplied key/value metadata attached to the run record for attribution — what this measurement was of (the commit or release candidate being scored, the CI job that asked for it). Round-trips verbatim on every read of the run, the list included.\n\nThe bag is caller-owned and no key is reserved: everything the platform decides about a run (`status`, `agent_version`, `baseline_run_id`, `aggregate_scores`, `passed`, the counts) is a field of its own and cannot be written from here. Nothing in the scoring path reads it. A non-object is rejected with `400 VALIDATION_FAILED` and no run is created.","required":false,"type":"object","in":"body"},{"name":"tool_context","description":"Key/value context forwarded to every item's generation, so an agent whose tools authorize through `tool_context` is scored against the configuration it runs in production rather than with an empty bag. Each key is forwarded as one `X-Soat-Context-<key>` header and resolves any `{{context:<key>}}` token in a bound tool's headers or `preset_parameters`.\n\nStored on the run and re-read per item, since a queued run (the default) is driven by a worker with no request behind it. **Write-only**: no read of the run returns it, unlike `metadata` — a run is a report other people read, and a credential in it is not theirs to see. Cleared once the run reaches a terminal state.\n\nAn eval generation has no session, so the reserved keys `session_id`, `actor_id` and `actor_external_id` are dropped (in any casing) rather than forwarded. Every other key becomes an HTTP header name and must match that grammar, or the request is rejected with `400 INVALID_TOOL_CONTEXT_KEY` and no run is created.","required":false,"type":"object","in":"body"}]},"get-eval-run":{serviceClass:"Evaluations",operationId:"getEvalRun",description:"Returns a run's status, counts, and aggregate scores",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"get",pathParams:["eval_id","eval_run_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"eval_id","description":"Eval ID","required":true,"type":"string","in":"path"},{"name":"eval_run_id","description":"Eval run ID","required":true,"type":"string","in":"path"}]},"list-eval-results":{serviceClass:"Evaluations",operationId:"listEvalResults",description:"Returns the per-item results of a run, oldest first",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"get",pathParams:["eval_id","eval_run_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"eval_id","description":"Eval ID","required":true,"type":"string","in":"path"},{"name":"eval_run_id","description":"Eval run ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"cancel-eval-run":{serviceClass:"Evaluations",operationId:"cancelEvalRun",description:"Cancels a queued or running run: its outstanding item tasks are dropped so it stops consuming provider budget, and the run settles as `canceled`. Results already written are kept — they are real measurements of generations that were really paid for — and `completed_count` / `errored_count` report what ran. `aggregate_scores` is deliberately left null: a partial roll-up in the same field a completed run uses would read as a whole-dataset verdict. A run that has already finished is rejected with 400.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"post",pathParams:["eval_id","eval_run_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"eval_id","description":"Eval ID","required":true,"type":"string","in":"path"},{"name":"eval_run_id","description":"Eval run ID","required":true,"type":"string","in":"path"}]},"list-exceptions":{serviceClass:"Exceptions",operationId:"listExceptions",description:"Returns exception items for a project, filterable by status, severity, and kind.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/exceptions",httpMethod:"get",pathParams:[],queryParams:["project_id","status","severity","kind","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"status","description":"Filter by triage status","required":false,"type":"string","in":"query"},{"name":"severity","description":"Filter by severity","required":false,"type":"string","in":"query"},{"name":"kind","description":"Filter by how the exception was filed","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-exception":{serviceClass:"Exceptions",operationId:"getException",description:"Returns a single exception item with its full detail.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/exceptions",httpMethod:"get",pathParams:["exception_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"exception_id","description":"Exception item ID","required":true,"type":"string","in":"path"}]},"acknowledge-exception":{serviceClass:"Exceptions",operationId:"acknowledgeException",description:"Moves the item to `acknowledged` (\"someone is on it\"), recording who. A no-op that returns the item unchanged when already acknowledged; rejected when already resolved.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/exceptions",httpMethod:"post",pathParams:["exception_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"exception_id","description":"Exception item ID","required":true,"type":"string","in":"path"}]},"resolve-exception":{serviceClass:"Exceptions",operationId:"resolveException",description:"Moves the item to `resolved` (\"fixed\"), recording who and an optional note.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/exceptions",httpMethod:"post",pathParams:["exception_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"exception_id","description":"Exception item ID","required":true,"type":"string","in":"path"},{"name":"note","description":"Optional resolution note","required":false,"type":"string","in":"body"}]},"list-files":{serviceClass:"Files",operationId:"listFiles",description:"Returns a list of all stored files",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"get",pathParams:[],queryParams:["project_id","path_prefix","tags","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Filter files by project ID","required":false,"type":"string","in":"query"},{"name":"path_prefix","description":"Only files under this directory. The prefix is a path boundary, not a substring: `/reports` returns `/reports/q1.txt` and never `/reports-archive/q1.txt`, and `/` selects the whole project. A leading slash is optional and a trailing one is ignored, so `reports`, `/reports` and `/reports/` are the same filter. `%` and `_` are literal characters, not wildcards. Naming a directory under `/.system/` is what includes platform-written files, which a list without this parameter leaves out.","required":false,"type":"string","in":"query"},{"name":"tags","description":"Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n","required":false,"type":"array","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-file":{serviceClass:"Files",operationId:"createFile",description:"Creates a new file record in the system",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project. Optional when authenticating with a project-scoped API key, which defaults to the key's project; required otherwise.","required":false,"type":"string","in":"body"},{"name":"prefix","description":"Directory within the project (e.g. /images). Optional; defaults to / (root). Combined with filename to form the file's key (path).","required":false,"type":"string","in":"body"},{"name":"filename","description":"Original / download name and the key's leaf segment (e.g. logo.png).","required":false,"type":"string","in":"body"},{"name":"content_type","description":"MIME type of the file","required":false,"type":"string","in":"body"},{"name":"size","description":"File size in bytes","required":false,"type":"integer","in":"body"},{"name":"metadata","description":"Caller-owned annotations on a resource, stored as the object they were written as: the types a value was written with are the types a read returns, so a filter can ask an ordering question about a number. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.\n\nNo key is reserved, and that is the point: every piece of state the platform owns lives in its own typed column, so nothing written here reaches platform state. The platform never reads the bag — it is not an IAM context, not a policy input and not part of a prompt — which is what separates it from a tag bag.","required":false,"type":"object","in":"body"}]},"upload-file":{serviceClass:"Files",operationId:"uploadFile",description:"Uploads a file to the server and stores it in the configured storage directory",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[]},"upload-file-base64":{serviceClass:"Files",operationId:"uploadFileBase64",description:"Uploads a file to the server using base64-encoded content",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project","required":false,"type":"string","in":"body"},{"name":"content","description":"Base64-encoded file content","required":true,"type":"string","in":"body"},{"name":"prefix","description":"Directory within the project (e.g. /documents). Optional; defaults to / (root).","required":false,"type":"string","in":"body"},{"name":"filename","description":"Original / download name and the key's leaf segment.","required":false,"type":"string","in":"body"},{"name":"content_type","description":"MIME type of the file","required":false,"type":"string","in":"body"},{"name":"metadata","description":"Caller-owned annotations on a resource, stored as the object they were written as: the types a value was written with are the types a read returns, so a filter can ask an ordering question about a number. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.\n\nNo key is reserved, and that is the point: every piece of state the platform owns lives in its own typed column, so nothing written here reaches platform state. The platform never reads the bag — it is not an IAM context, not a policy input and not part of a prompt — which is what separates it from a tag bag.","required":false,"type":"object","in":"body"}]},"create-presigned-url":{serviceClass:"Files",operationId:"createPresignedUrl",description:"Creates a short-lived, single-use presigned upload URL — the local-storage equivalent of an S3 presigned URL. The client then uploads the file content directly to the returned `upload_url` via `POST /api/v1/files/upload/{token}`, bypassing MCP payload size limits. When the server is configured with `SOAT_BASE_URL`, `upload_url` is a fully-qualified absolute URL so MCP agents and other clients can POST to it without knowing the server base URL in advance.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project","required":true,"type":"string","in":"body"},{"name":"prefix","description":"Directory within the project (e.g. /documents). Optional; defaults to / (root).","required":false,"type":"string","in":"body"},{"name":"filename","description":"Original / download name and the key's leaf segment.","required":false,"type":"string","in":"body"},{"name":"content_type","description":"MIME type of the file to be uploaded","required":false,"type":"string","in":"body"}]},"upload-file-with-token":{serviceClass:"Files",operationId:"uploadFileWithToken",description:"Uploads file content authorized by a single-use token from `POST /api/v1/files/presigned-url`. No bearer credential is required — the token is the credential. Accepts either multipart/form-data (field `file`) or JSON with a base64-encoded `content` field.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"post",pathParams:["token"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"token","description":"The single-use upload token (e.g. upt_...)","required":true,"type":"string","in":"path"},{"name":"content","description":"Base64-encoded file content (alternative to multipart `file`)","required":false,"type":"string","in":"body"},{"name":"filename","description":"Original / download name (overrides the token's filename)","required":false,"type":"string","in":"body"},{"name":"content_type","description":"MIME type of the file (overrides the token's content type)","required":false,"type":"string","in":"body"},{"name":"metadata","description":"Caller-owned annotations on a resource, stored as the object they were written as: the types a value was written with are the types a read returns, so a filter can ask an ordering question about a number. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.\n\nNo key is reserved, and that is the point: every piece of state the platform owns lives in its own typed column, so nothing written here reaches platform state. The platform never reads the bag — it is not an IAM context, not a policy input and not part of a prompt — which is what separates it from a tag bag.","required":false,"type":"object","in":"body"}]},"get-file":{serviceClass:"Files",operationId:"getFile",description:"Returns the data and metadata of a specific file",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"get",pathParams:["file_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"file_id","description":"File ID","required":true,"type":"string","in":"path"}]},"delete-file":{serviceClass:"Files",operationId:"deleteFile",description:"Removes a file from the system by ID",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"delete",pathParams:["file_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"file_id","description":"ID of the file to delete","required":true,"type":"string","in":"path"}]},"download-file":{serviceClass:"Files",operationId:"downloadFile",description:"Streams the file content to the client",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"get",pathParams:["file_id"],queryParams:["token"],headerParams:[],cookieParams:[],flags:[{"name":"file_id","description":"File ID","required":true,"type":"string","in":"path"},{"name":"token","description":"Signed single-file download token, an alternative to a bearer credential (issued for ingestion-rule converters).","required":false,"type":"string","in":"query"}]},"update-file-metadata":{serviceClass:"Files",operationId:"updateFileMetadata",description:"Updates the metadata field of a file",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"patch",pathParams:["file_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"file_id","description":"File ID","required":true,"type":"string","in":"path"},{"name":"metadata","description":"Replaces the stored bag; `null` clears it.","required":false,"type":"object","in":"body"},{"name":"prefix","description":"New directory — moves the file. The resulting path (prefix + filename) must be unique within the project.","required":false,"type":"string","in":"body"},{"name":"filename","description":"New filename — renames the key's leaf and the download name.","required":false,"type":"string","in":"body"}]},"download-file-base64":{serviceClass:"Files",operationId:"downloadFileBase64",description:"Returns the file content encoded as base64",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"get",pathParams:["file_id"],queryParams:["token"],headerParams:[],cookieParams:[],flags:[{"name":"file_id","description":"File ID","required":true,"type":"string","in":"path"},{"name":"token","description":"Signed single-file download token, an alternative to a bearer credential (issued for ingestion-rule converters).","required":false,"type":"string","in":"query"}]},"get-file-tags":{serviceClass:"Files",operationId:"getFileTags",description:"Returns all tags attached to the file",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"get",pathParams:["file_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"file_id","description":"File ID","required":true,"type":"string","in":"path"}]},"replace-file-tags":{serviceClass:"Files",operationId:"replaceFileTags",description:"Replaces all tags on the file with the provided tags",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"put",pathParams:["file_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"file_id","description":"File ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"merge-file-tags":{serviceClass:"Files",operationId:"mergeFileTags",description:"Merges provided tags into the file's existing tags (existing tags are preserved unless overridden)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"patch",pathParams:["file_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"file_id","description":"File ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"validate-formation":{serviceClass:"Formations",operationId:"validateFormation",description:"Validates a formation template without creating any resources. Returns a list of errors and warnings. Accepts the template as a JSON object or as a YAML/JSON string.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/formations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"template","description":"A formation template supplied as either a JSON object or a YAML/JSON string. When a string is provided the server parses it with a YAML parser (JSON is valid YAML) before processing.\n","required":false,"type":"unknown","in":"body"},{"name":"parameters","description":"Runtime parameter values that override or supply template parameter defaults. Keys must match parameter names declared in `template.parameters`. When provided, the validation result also reports required parameters that are still missing after applying these values.\n","required":false,"type":"object","in":"body"}]},"plan-formation":{serviceClass:"Formations",operationId:"planFormation",description:"Computes a diff between the desired template and the current stack state without making any changes. Returns the list of planned actions.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/formations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID. Optional when authenticating with a project-scoped API key, which defaults to the key's project; required otherwise.","required":false,"type":"string","in":"body"},{"name":"formation_id","description":"Existing formation ID to compare against. Omit for new formation planning.","required":false,"type":"string","in":"body"},{"name":"template","description":"A formation template supplied as either a JSON object or a YAML/JSON string. When a string is provided the server parses it with a YAML parser (JSON is valid YAML) before processing.\n","required":true,"type":"unknown","in":"body"},{"name":"parameters","description":"Runtime parameter values that override or supply template parameter defaults. Keys must match parameter names declared in `template.parameters`. A parameter declared with `use_previous_value: true` may be omitted to reuse its stored value.\n","required":false,"type":"object","in":"body"}]},"list-formations":{serviceClass:"Formations",operationId:"listFormations",description:"Returns all formation stacks for a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/formations",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-formation":{serviceClass:"Formations",operationId:"createFormation",description:"Validates the template, creates the formation record, then provisions all declared resources in dependency order. A **template-shape** error is refused with `400`. A **deploy** failure is not: the operation ran, so the formation is returned with `201` and `status: \"failed\"`, and `error` explains why (the resources created before the failure are rolled back). Read `status` — a `2xx` here means the deploy was attempted, not that it worked. The `builtin` CLI exits non-zero on that body so `create-formation && …` does not lie.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/formations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID. Optional when authenticating with a project-scoped API key, which defaults to the key's project; required otherwise.","required":false,"type":"string","in":"body"},{"name":"name","description":"Human-readable name for the formation stack","required":true,"type":"string","in":"body"},{"name":"template","description":"A formation template supplied as either a JSON object or a YAML/JSON string. When a string is provided the server parses it with a YAML parser (JSON is valid YAML) before processing.\n","required":true,"type":"unknown","in":"body"},{"name":"parameters","description":"Runtime parameter values that override or supply template parameter defaults. Keys must match parameter names declared in `template.parameters`. Required parameters (those without a default) must be provided here.\n","required":false,"type":"object","in":"body"},{"name":"metadata","description":"Static annotations stored on the formation record. This field is NOT a substitution site: `sub`/`param`/`ref` expressions are rejected with 400 (`FORMATION_INVALID_METADATA`). For deploy-time substitution use the template's top-level `metadata` block, which is resolved into `resolved_metadata`.\n","required":false,"type":"object","in":"body"}]},"get-formation":{serviceClass:"Formations",operationId:"getFormation",description:"Returns the formation stack including its current resources.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/formations",httpMethod:"get",pathParams:["formation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"formation_id","description":"","required":true,"type":"string","in":"path"}]},"update-formation":{serviceClass:"Formations",operationId:"updateFormation",description:"Applies a new template to the formation. Resources are created, updated, or deleted to reconcile the current state with the desired state. A **template-shape** error is refused with `400`. A **deploy** failure is not: the operation ran, so the formation is returned with `200` and `status: \"failed\"`, and `error` explains why. Read `status` — a `2xx` here means the deploy was attempted, not that it worked. The `builtin` CLI exits non-zero on that body so `update-formation && …` does not lie. A deploy that replaced a resource and could not delete the superseded one answers `status: \"active\"` with `error.code: \"FORMATION_REPLACE_CLEANUP_FAILED\"` — the desired state is realised, and `error.meta.failures` names every resource still live. The next deploy retries the disposal.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/formations",httpMethod:"put",pathParams:["formation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"formation_id","description":"","required":true,"type":"string","in":"path"},{"name":"template","description":"A formation template supplied as either a JSON object or a YAML/JSON string. When a string is provided the server parses it with a YAML parser (JSON is valid YAML) before processing.\n","required":false,"type":"unknown","in":"body"},{"name":"parameters","description":"Runtime parameter values that override or supply template parameter defaults. Keys must match parameter names declared in `template.parameters`. Required parameters (those without a default) must be provided here, unless the parameter is declared with `use_previous_value: true`, in which case omitting it reuses the previously stored value.\n","required":false,"type":"object","in":"body"},{"name":"metadata","description":"Static annotations stored on the formation record. This field is NOT a substitution site: `sub`/`param`/`ref` expressions are rejected with 400 (`FORMATION_INVALID_METADATA`). For deploy-time substitution use the template's top-level `metadata` block, which is resolved into `resolved_metadata`.\n","required":false,"type":"object","in":"body"}]},"delete-formation":{serviceClass:"Formations",operationId:"deleteFormation",description:"Deletes the formation stack and all its managed resources in reverse dependency order. A resource the platform refuses to delete on its own — most often an agent that has generation or trace history — fails the teardown with `409 FORMATION_DELETE_FAILED`, naming every blocking resource in `error.meta.failures`. Resolve the blockers (for an agent, `DELETE /api/v1/agents/{agent_id}?force=true` also removes its generations and traces, and `deletion_policy: retain` exempts it from teardown entirely) and delete the formation again. A refusal the platform can foresee is found by a pre-flight, before the first delete: nothing is removed, and the formation stays `active` and intact for the retry. An unforeseeable error surfaces mid-teardown instead, where resources deleted before the blocker stay deleted and the formation is left in `delete_failed`. The error message states which happened.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/formations",httpMethod:"delete",pathParams:["formation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"formation_id","description":"","required":true,"type":"string","in":"path"}]},"list-formation-events":{serviceClass:"Formations",operationId:"listFormationEvents",description:"Returns all operations (create, update, delete) with their event logs for the formation, ordered chronologically.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/formations",httpMethod:"get",pathParams:["formation_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"formation_id","description":"","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"list-generations":{serviceClass:"Generations",operationId:"listGenerations",description:"Returns generations the caller can access, optionally filtered by agent, trace, orchestration run, node, and status. The generations of one trace are the `trace_id` filter. Filtering by `orchestration_run_id` is the supported way to get from an orchestration run to the generations its agent nodes produced: a node execution record carries no generation id, so the pointer lives here, alongside the run's other attribution columns.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/generations",httpMethod:"get",pathParams:[],queryParams:["agent_id","trace_id","session_id","actor_id","initiator_generation_id","chain_id","orchestration_run_id","node_id","status","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Filter by agent public ID","required":false,"type":"string","in":"query"},{"name":"trace_id","description":"Filter by trace public ID","required":false,"type":"string","in":"query"},{"name":"session_id","description":"Return only the generations dispatched through one session — the turns behind that conversation's spend. A session that does not exist in scope yields an empty page.\n","required":false,"type":"string","in":"query"},{"name":"actor_id","description":"Return only the generations one end user started, across every session they appear in. An actor that does not exist in scope yields an empty page.\n","required":false,"type":"string","in":"query"},{"name":"initiator_generation_id","description":"Filter by the public ID of the parent generation. Returns every generation started by that generation: sub-agent invocations, approval continuations, client-tool re-handoffs and memory-rule handler turns. Top-level generations are not returned.\n","required":false,"type":"string","in":"query"},{"name":"chain_id","description":"Filter by the continuation chain the generation belongs to. This is how a chain is expanded into its members — the chain record carries only their count.\n","required":false,"type":"string","in":"query"},{"name":"orchestration_run_id","description":"Filter by the orchestration run that dispatched the generation. This is how a run is traced back to what its agent nodes did — a node execution record stores no generation id.\n","required":false,"type":"string","in":"query"},{"name":"node_id","description":"Filter by the orchestration node that dispatched the generation. Combine with `orchestration_run_id` to narrow to one node of one run; a retried node returns one generation per `node_attempt`.\n","required":false,"type":"string","in":"query"},{"name":"status","description":"Filter by lifecycle status","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-generation":{serviceClass:"Generations",operationId:"getGeneration",description:"Returns a single generation record by ID, including its status and the structured `error` payload when the generation failed (e.g. because the upstream AI provider returned an error).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/generations",httpMethod:"get",pathParams:["generation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"generation_id","description":"Public ID of the generation","required":true,"type":"string","in":"path"}]},"update-generation":{serviceClass:"Generations",operationId:"updateGeneration",description:"Attaches caller-supplied key/value metadata to a generation record for per-run audit attribution (e.g. the ticket or case an AI action belongs to). The provided keys are shallow-merged over the existing `metadata`, so repeated patches accumulate. The bag is caller-owned and no key is reserved: server-owned state (usage attribution, the served agent version, the route's record, the extraction summary, what knowledge retrieval served) lives in its own top-level fields and cannot be written from here.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/generations",httpMethod:"patch",pathParams:["generation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"generation_id","description":"Public ID of the generation","required":true,"type":"string","in":"path"},{"name":"metadata","description":"Caller-supplied key/value metadata to shallow-merge into the generation record's caller-owned `metadata` bag. No key is reserved: server-owned state lives in its own top-level fields and cannot be written from here.\n","required":true,"type":"object","in":"body"}]},"purge-generation-content":{serviceClass:"Generations",operationId:"purgeGenerationContent",description:"Clears the generation's content — `metadata`, `error`, `extraction`, and the internal recovery state of a paused run — and stamps `content_redacted_at`. The usage and audit skeleton is preserved: ids, timestamps, status, stop reason, and the attribution fields (`action_id`, `trigger_id`, `orchestration_run_id`, `node_id`, `node_attempt`, `agent_version`, `routing`) the billing ledger reads. A purged generation reads back as that skeleton, not a 404. This does **not** delete the parent trace's steps object, which holds this generation's content alongside its siblings'. To erase the run's content completely, purge the trace (`DELETE /api/v1/traces/{trace_id}/content`), which cascades here. Idempotent — purging an already-purged generation succeeds and leaves the original `content_redacted_at` in place.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/generations",httpMethod:"delete",pathParams:["generation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"generation_id","description":"Public ID of the generation","required":true,"type":"string","in":"path"}]},"list-generation-memory-assertions":{serviceClass:"Generations",operationId:"listGenerationMemoryAssertions",description:"Every memory write this turn made, oldest first: each memory rule's firings and the agent's own `write_memory` calls alike. The rows behind the generation's per-rule `extraction` counts. Reading them is reading the generation (`generations:GetGeneration`).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/generations",httpMethod:"get",pathParams:["generation_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"generation_id","description":"Public ID of the generation","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-generation-transcript":{serviceClass:"Generations",operationId:"getGenerationTranscript",description:"Returns one generation's turn read back as an ordered sequence of steps: what it was asked, each model step with its tool calls and results, and how it ended. The transcript is assembled at read time from the generation record and the trace's steps object; nothing is stored, so it cannot outlive the content it projects. Requires `traces:GetTrace` in addition to `generations:GetGeneration`, because the response merges content from both resources. A generation whose content is unavailable — never written under zero-retention, or cleared by a purge — returns `200` with the skeleton rather than an error: `input` and `output` are null, `steps` is empty, and the `content_redacted_*` fields say which happened. `content_redacted_by_principal_id` is `zero_retention` when the content was never stored, and the purging principal's ID when it was erased later. A generation that is still running returns the same shape with an empty `steps`; `status` disambiguates the two.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/generations",httpMethod:"get",pathParams:["generation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"generation_id","description":"Public ID of the generation","required":true,"type":"string","in":"path"}]},"list-guardrails":{serviceClass:"Guardrails",operationId:"listGuardrails",description:"Returns all guardrails in the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID to filter by","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-guardrail":{serviceClass:"Guardrails",operationId:"createGuardrail",description:"Creates a new guardrail in the project, archiving its document as version 1. The `document` is validated on write: `class` must be a literal (A/B/C/D) or a JSON Logic expression, and every variable it (and `guard`) reference must resolve to the `args.*` / `context.*` / `runtime.*` namespaces — an out-of-catalog `runtime.*` key is rejected with 400.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project","required":false,"type":"string","in":"body"},{"name":"name","description":"Human-readable name","required":true,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"document","description":"The action-class document. `class` maps a call to an action class; `guard` gates class-B autonomy. Both are single JSON Logic expressions over the `args.*` / `context.*` / `runtime.*` namespaces.\n","required":true,"type":"object","in":"body"},{"name":"context_tool_id","description":"","required":false,"type":"string","in":"body"},{"name":"context_mode","description":"","required":false,"type":"string","in":"body"},{"name":"version_label","description":"Optional tag for the config version this write archives (e.g. `initial`). Annotates the version only — it is not stored on the guardrail and is not part of the config, so labelling a change is never itself a change.","required":false,"type":"string","in":"body"}]},"get-guardrail":{serviceClass:"Guardrails",operationId:"getGuardrail",description:"Returns a single guardrail by ID.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"get",pathParams:["guardrail_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"guardrail_id","description":"","required":true,"type":"string","in":"path"}]},"update-guardrail":{serviceClass:"Guardrails",operationId:"updateGuardrail",description:"Updates an existing guardrail. A `document` write that actually changes the policy increments `version` and archives the new document as a GuardrailVersion; metadata-only edits (name / description / context), and re-writing the document the guardrail already holds, leave the version untouched.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"patch",pathParams:["guardrail_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"guardrail_id","description":"","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"document","description":"The action-class document. `class` maps a call to an action class; `guard` gates class-B autonomy. Both are single JSON Logic expressions over the `args.*` / `context.*` / `runtime.*` namespaces.\n","required":false,"type":"object","in":"body"},{"name":"context_tool_id","description":"","required":false,"type":"string","in":"body"},{"name":"context_mode","description":"","required":false,"type":"string","in":"body"},{"name":"version_label","description":"Optional tag for the config version this write archives (e.g. `pre-tightening`). Annotates the version only — it is not stored on the guardrail and is not part of the config, so labelling a change is never itself a change. Ignored when the write changes no policy, since no version is created.","required":false,"type":"string","in":"body"},{"name":"expected_version","description":"Refuses the write unless the resource is at this version.","required":false,"type":"integer","in":"body"}]},"delete-guardrail":{serviceClass:"Guardrails",operationId:"deleteGuardrail",description:"Deletes a guardrail and its archived versions by ID.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"delete",pathParams:["guardrail_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"guardrail_id","description":"","required":true,"type":"string","in":"path"}]},"list-guardrail-versions":{serviceClass:"Guardrails",operationId:"listGuardrailVersions",description:"Returns the guardrail's archived configurations, newest first. A version is written on create and on every subsequent write that changes the policy `document` — through the REST API or a formation apply alike. Metadata-only edits (name, description, context binding) do not archive a version. See [Versioning](/docs/modules/guardrails#versioning).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"get",pathParams:["guardrail_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"guardrail_id","description":"","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-guardrail-version":{serviceClass:"Guardrails",operationId:"getGuardrailVersion",description:"Returns the exact configuration — and so the exact `document` — that governed at a given version. Approval items, activity entries, and exceptions record the version that governed them, so the audit chain survives edits.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"get",pathParams:["guardrail_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"guardrail_id","description":"","required":true,"type":"string","in":"path"},{"name":"version","description":"","required":true,"type":"integer","in":"path"}]},"restore-guardrail-version":{serviceClass:"Guardrails",operationId:"restoreGuardrailVersion",description:"Writes an archived version's `document` back as the guardrail's live policy, which archives it again as a **new** version rather than rewinding the counter — so an approval item or exception citing any version in between still resolves. The restore runs through the ordinary update path, so the archived document is re-validated; restoring the policy the guardrail already holds is a no-op and archives nothing.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"post",pathParams:["guardrail_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"guardrail_id","description":"","required":true,"type":"string","in":"path"},{"name":"version","description":"","required":true,"type":"integer","in":"path"},{"name":"label","description":"Optional tag for the version the restore creates. Defaults to `restored from v<version>`.","required":false,"type":"string","in":"body"}]},"evaluate-guardrail":{serviceClass:"Guardrails",operationId:"evaluateGuardrail",description:"Runs the full evaluation pipeline — the `class` expression, the guard, the context tool per `context_mode`, live `runtime.*` resolution — against caller-supplied `args` and `guardrail_context`, and returns the exact `guardrail_evaluation` record a real call would produce. Nothing executes, no approval item is filed, and no activity entry is written. This is the adoption path: preview a document's decisions against production-shaped calls before attaching it — or before editing a widely-attached one. Pass an optional `tool_id` to resolve `runtime.tools.*`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"post",pathParams:["guardrail_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"guardrail_id","description":"","required":true,"type":"string","in":"path"},{"name":"args","description":"The proposed call's arguments (the `args.*` namespace).","required":false,"type":"object","in":"body"},{"name":"guardrail_context","description":"The caller-supplied guardrail context (the `context.*` namespace), combined with the context tool per `context_mode`.\n","required":false,"type":"object","in":"body"},{"name":"tool_id","description":"Optional tool to resolve `runtime.tools.*` against.","required":false,"type":"string","in":"body"}]},"list-ingestion-rules":{serviceClass:"IngestionRules",operationId:"listIngestionRules",description:"Returns the ingestion rules for a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ingestion-rules",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-ingestion-rule":{serviceClass:"IngestionRules",operationId:"createIngestionRule",description:"Creates a rule mapping a content_type glob to a converter. Exactly one of tool_id or agent_id must be set.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ingestion-rules",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"content_type_glob","description":"MIME type glob matched against a file's content_type. At most 4 wildcards and 255 characters — a MIME glob needs one on each side of the slash at most.","required":true,"type":"string","in":"body"},{"name":"tool_id","description":"Converter tool id (mutually exclusive with agent_id)","required":false,"type":"string","in":"body"},{"name":"agent_id","description":"Converter agent id (mutually exclusive with tool_id)","required":false,"type":"string","in":"body"},{"name":"action","description":"Operation id, required for builtin/mcp tool converters","required":false,"type":"string","in":"body"},{"name":"preset_parameters","description":"Merged into the tool input before invocation (tool converters only)","required":false,"type":"object","in":"body"},{"name":"native_extraction","description":"For native types (PDF/text): `first` (default) converts only when native extraction yields no text; `skip` always converts.","required":false,"type":"string","in":"body"},{"name":"file_delivery","description":"How the file reaches a tool converter (default base64)","required":false,"type":"string","in":"body"},{"name":"chunk_strategy","description":"Default chunk strategy, overridable per ingest request","required":false,"type":"string","in":"body"},{"name":"chunk_size","description":"Default window size in characters for the size strategy","required":false,"type":"integer","in":"body"},{"name":"chunk_overlap","description":"Default overlap in characters for the size strategy","required":false,"type":"integer","in":"body"},{"name":"metadata","description":"Arbitrary JSON metadata","required":false,"type":"object","in":"body"}]},"get-ingestion-rule":{serviceClass:"IngestionRules",operationId:"getIngestionRule",description:"Returns a specific ingestion rule",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ingestion-rules",httpMethod:"get",pathParams:["ingestion_rule_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ingestion_rule_id","description":"Ingestion rule ID","required":true,"type":"string","in":"path"}]},"update-ingestion-rule":{serviceClass:"IngestionRules",operationId:"updateIngestionRule",description:"Updates fields of an ingestion rule",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ingestion-rules",httpMethod:"patch",pathParams:["ingestion_rule_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ingestion_rule_id","description":"Ingestion rule ID","required":true,"type":"string","in":"path"},{"name":"content_type_glob","description":"MIME type glob matched against a file's content_type. At most 4 wildcards and 255 characters.","required":false,"type":"string","in":"body"},{"name":"tool_id","description":"","required":false,"type":"string","in":"body"},{"name":"agent_id","description":"","required":false,"type":"string","in":"body"},{"name":"action","description":"","required":false,"type":"string","in":"body"},{"name":"preset_parameters","description":"","required":false,"type":"object","in":"body"},{"name":"native_extraction","description":"","required":false,"type":"string","in":"body"},{"name":"file_delivery","description":"","required":false,"type":"string","in":"body"},{"name":"chunk_strategy","description":"Send `null` to clear the rule's override and fall back to the per-request default.","required":false,"type":"string","in":"body"},{"name":"chunk_size","description":"","required":false,"type":"integer","in":"body"},{"name":"chunk_overlap","description":"","required":false,"type":"integer","in":"body"},{"name":"metadata","description":"A `MetadataBag` on a field where `null` is meaningful — a full-replacement update that clears the bag, or a record whose bag was never set.","required":false,"type":"object","in":"body"}]},"delete-ingestion-rule":{serviceClass:"IngestionRules",operationId:"deleteIngestionRule",description:"Deletes an ingestion rule",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ingestion-rules",httpMethod:"delete",pathParams:["ingestion_rule_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ingestion_rule_id","description":"Ingestion rule ID","required":true,"type":"string","in":"path"}]},"search-knowledge":{serviceClass:"Knowledge",operationId:"searchKnowledge",description:"Searches across documents and memories using hybrid retrieval — a vector query and a full-text query per source, fused by reciprocal rank — or by file paths, document IDs, memory store IDs, or tags. At least one of `query`, `tags`, `document_paths`, `document_ids`, or `memory_store_ids` must be provided.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/knowledge",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Limit search to a specific project","required":false,"type":"string","in":"body"},{"name":"query","description":"Search query text. Runs a vector query and a full-text query over each store and fuses the rankings; the full-text channel matches exact tokens and phrases, the vector channel everything else.","required":false,"type":"string","in":"body"},{"name":"min_similarity","description":"Minimum raw cosine `similarity_score` a **vector** candidate must reach to take part in ranking, applied before fusion. Only applies when `query` is provided. Lexical candidates are deliberately exempt: a result that literally contains the searched token is the evidence, and dropping it for a low cosine is the failure hybrid search exists to prevent. Not a floor on `score`, whose fused value encodes rank position rather than similarity.","required":false,"type":"number","in":"body"},{"name":"rrf_k","description":"The `k` in the reciprocal rank fusion term `1 / (k + rank)`, which sets how steeply a result's contribution decays with its position in each ranked list. A smaller value weights the very top of each list more heavily. Defaults to the deployment's `KNOWLEDGE_RRF_K`, itself 60 by default. Only applies when `query` is provided.","required":false,"type":"integer","in":"body"},{"name":"recency_half_life_days","description":"Half-life, in days, of a recency decay applied to **memory store** results after fusion: a result's `score` is multiplied by `2 ^ (-age_in_days / recency_half_life_days)`, where age is measured from its `updated_at`. Document results are never decayed. `0` — the default, and the default of the deployment's `KNOWLEDGE_RECENCY_HALF_LIFE_DAYS` — disables the blend entirely; it is a switch, not a lower bound, and sending `0` turns off a deployment-wide decay for this one request. Accepts fractions (`0.5` is twelve hours). How many ranks a given half-life costs depends on `rrf_k`. Only applies when `query` is provided. **No value is known to be safe in general**: every half-life measured against the reference corpus that lifted recency-sensitive queries also cost relevance-sensitive ones, which is why this ships disabled. Measure against your own corpus before setting it — see the Retrieval Quality guide.","required":false,"type":"number","in":"body"},{"name":"limit","description":"Maximum number of results to return (default 10). A value above 100 is clamped to 100 — the ceiling bounds the vector scan this one request performs, so a larger `limit` returns everything there is up to that many rows rather than being refused.","required":false,"type":"integer","in":"body"},{"name":"include_documents","description":"Set `false` to leave the document store out of this search. A `query` names no store, so it reaches both; the store-specific filters narrow *within* a store rather than choosing between them. This is the switch, and a filter naming the other store never overrides it. `false` for both stores is `400`.","required":false,"type":"boolean","in":"body"},{"name":"include_memories","description":"Set `false` to leave the memory store out of this search. The mirror of `include_documents`, for a caller that wants documents alone.","required":false,"type":"boolean","in":"body"},{"name":"memory_store_ids","description":"Search memories within these specific memory stores","required":false,"type":"array","in":"body"},{"name":"document_paths","description":"Filter results to documents whose file path starts with one of these prefixes","required":false,"type":"array","in":"body"},{"name":"document_ids","description":"Filter results to specific document IDs","required":false,"type":"array","in":"body"},{"name":"tags","description":"Filter results to documents and memories whose `tags` contain every one of these key-value pairs (exact, case-sensitive match). Scopes both stores, so passing it alone searches both — as `query` does. For memories it matches at memory granularity: a memory is returned when its parent memory store's tags match or its own do.","required":false,"type":"object","in":"body"},{"name":"metadata","description":"Filter document results by their `metadata` bag. A document-store filter: a memory carries no such bag, so passing it alone searches documents, as `document_paths` does.","required":false,"type":"object","in":"body"}]},"list-memories":{serviceClass:"Memories",operationId:"listMemories",description:"Returns all memories in a memory store",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"get",pathParams:[],queryParams:["memory_store_id","tags","limit","offset","include_invalidated"],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"Memory store to list memories from (mstore_...)","required":true,"type":"string","in":"query"},{"name":"tags","description":"Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n","required":false,"type":"array","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"include_invalidated","description":"Include invalidated memories — superseded or retracted. They are excluded by default; set this to audit what a store once held.","required":false,"type":"boolean","in":"query"}]},"create-memory":{serviceClass:"Memories",operationId:"createMemory",description:"Writes a fact to the specified memory store through the standard write algorithm: the content is embedded (or matched to text the store already holds), compared against the most similar currently-valid memory, and resolved to exactly one of three outcomes — `skipped` at or above `duplicate_threshold`, `superseded` at or above `supersede_threshold`, `created` below it. `supersedes` overrides that comparison entirely, retiring the memory it names. Every call records one assertion, whatever the outcome.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"Memory store to add the memory to (mstore_...)","required":true,"type":"string","in":"body"},{"name":"content","description":"The text content of the memory","required":true,"type":"string","in":"body"},{"name":"source_type","description":"Whether there is a source to point at. `conversation` requires `source_id`; `manual` rejects it.","required":false,"type":"string","in":"body"},{"name":"source_id","description":"The conversation this fact was learned in. Required when `source_type` is `conversation`, and rejected otherwise.","required":false,"type":"string","in":"body"},{"name":"tags","description":"Per-memory key-value tags, used for memory-granularity filtering by `tags` in search-knowledge.","required":false,"type":"object","in":"body"},{"name":"metadata","description":"Arbitrary structured metadata attached to the memory","required":false,"type":"object","in":"body"},{"name":"supersedes","description":"The memory this write replaces, named outright. The declaration outranks the thresholds in both directions: the target is retired and the write returns `superseded` however similar or distant the two texts are. This is what reaches a contradiction cosine cannot see (\"The office is in Lisbon\" then \"We closed the Lisbon office\"). The target must be a still-valid memory in the same memory store, and the caller needs `memories:UpdateMemory` on it as well as `memories:CreateMemory` on the store.","required":false,"type":"string","in":"body"},{"name":"duplicate_threshold","description":"Cosine similarity at or above which the incoming content is a duplicate of an existing memory and the write is skipped. Overrides the store's `duplicate_threshold` for this call; falls back to the store's value, then to `0.95`.","required":false,"type":"number","in":"body"},{"name":"supersede_threshold","description":"Cosine similarity at or above which the incoming content restates a fact that has changed: the top match is invalidated and a new memory replaces it. Overrides the store's `supersede_threshold` for this call; falls back to the store's value, then to `0.90`. The effective pair must satisfy `supersede_threshold < duplicate_threshold`, and the check runs against the effective pair — so overriding only one value cannot invert it against the store's other one.","required":false,"type":"number","in":"body"}]},"get-memory":{serviceClass:"Memories",operationId:"getMemory",description:"Returns a single memory by ID",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"get",pathParams:["memory_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_id","description":"","required":true,"type":"string","in":"path"}]},"update-memory":{serviceClass:"Memories",operationId:"updateMemory",description:"Updates an existing memory. Regenerates the embedding if content changes.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"put",pathParams:["memory_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"memory_id","description":"","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"content","description":"Updated text content","required":false,"type":"string","in":"body"},{"name":"tags","description":"Replaces the memory's tags. Pass null or an empty object to clear.","required":false,"type":"object","in":"body"},{"name":"metadata","description":"Replaces the memory's metadata. Pass null to clear.","required":false,"type":"object","in":"body"},{"name":"expected_version","description":"Refuses the write unless the memory is at this version.","required":false,"type":"integer","in":"body"}]},"delete-memory":{serviceClass:"Memories",operationId:"deleteMemory",description:"Deletes a memory",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"delete",pathParams:["memory_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_id","description":"","required":true,"type":"string","in":"path"}]},"retract-memory":{serviceClass:"Memories",operationId:"retractMemory",description:"Retires a fact that stopped holding with nothing replacing it. The memory leaves the default listing, [knowledge search](/docs/api/knowledge/search-knowledge) and write deduplication, so restating the fact later lands as a new memory. It is an invalidation with no successor, which is what tells it apart from a supersede: `invalidated_at` is set and `superseded_by_memory_id` stays null. The retraction is appended to the assertion ledger with outcome `retracted`, so who withdrew the fact is part of the record. The memory stays readable by id, with its text and its assertions. `DELETE` remains the way to remove it outright.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"post",pathParams:["memory_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"memory_id","description":"","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"expected_version","description":"Refuses the retraction unless the memory is at this version.","required":false,"type":"integer","in":"body"}]},"list-memory-assertions":{serviceClass:"Memories",operationId:"listMemoryAssertions",description:"Returns every write that resolved into this memory, oldest first: the one that created it, the duplicates it absorbed, and the assertion that superseded another memory in its favour. A superseded memory keeps its own assertions, so the chain can be walked in both directions.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"get",pathParams:["memory_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"memory_id","description":"","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-memory-tags":{serviceClass:"Memories",operationId:"getMemoryTags",description:"Returns all tags attached to the memory",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"get",pathParams:["memory_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_id","description":"Memory ID","required":true,"type":"string","in":"path"}]},"replace-memory-tags":{serviceClass:"Memories",operationId:"replaceMemoryTags",description:"Replaces all tags on the memory with the provided tags (not merged)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"put",pathParams:["memory_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_id","description":"Memory ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"merge-memory-tags":{serviceClass:"Memories",operationId:"mergeMemoryTags",description:"Merges provided tags into the memory's existing tags (existing tags are preserved unless overridden)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"patch",pathParams:["memory_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_id","description":"Memory ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"list-memory-rules":{serviceClass:"MemoryRules",operationId:"listMemoryRules",description:"Returns memory rules, newest first. Narrow to one store with `memory_store_id` — that form is authorized against the store itself, so it answers \"what feeds this store?\" in one call.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-rules",httpMethod:"get",pathParams:[],queryParams:["memory_store_id","project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"Only the rules of this memory store","required":false,"type":"string","in":"query"},{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-memory-rule":{serviceClass:"MemoryRules",operationId:"createMemoryRule",description:"Creates an ingestion rule on a memory store. With neither `agent_id` nor `tool_id` the built-in extractor runs, configurable through `prompt`, `ai_provider_id` and `model`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-rules",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"The destination store, and the rule's owning scope","required":true,"type":"string","in":"body"},{"name":"on","description":"The event a firing reads. `agents.generation.completed` fires once per completed turn whatever the transport, and is the only event the built-in extractor may bind to. `conversations.message.generated` fires per persisted assistant reply and is conversation-backed, so it is for custom handlers.","required":true,"type":"string","in":"body"},{"name":"source_agent_ids","description":"Agents whose turns this rule reads. Omit or send `null` for every agent in the store's project.","required":false,"type":"array","in":"body"},{"name":"agent_id","description":"Handler agent (mutually exclusive with tool_id)","required":false,"type":"string","in":"body"},{"name":"tool_id","description":"Handler tool (mutually exclusive with agent_id)","required":false,"type":"string","in":"body"},{"name":"action","description":"Operation id, for a tool handler","required":false,"type":"string","in":"body"},{"name":"preset_parameters","description":"Merged into a tool handler's input before invocation. The turn's own fields are reserved and win.","required":false,"type":"object","in":"body"},{"name":"prompt","description":"Replaces the built-in extractor's task instructions. The JSON response contract and the transcript are always appended. Not valid with a handler.","required":false,"type":"string","in":"body"},{"name":"ai_provider_id","description":"Provider override for the built-in extractor's completion. Not valid with a handler.","required":false,"type":"string","in":"body"},{"name":"model","description":"Model override for the built-in extractor's completion. Not valid with a handler.","required":false,"type":"string","in":"body"},{"name":"enabled","description":"A disabled rule is kept and never fires","required":false,"type":"boolean","in":"body"}]},"get-memory-rule":{serviceClass:"MemoryRules",operationId:"getMemoryRule",description:"Returns a specific memory rule",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-rules",httpMethod:"get",pathParams:["memory_rule_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_rule_id","description":"Memory rule ID","required":true,"type":"string","in":"path"}]},"update-memory-rule":{serviceClass:"MemoryRules",operationId:"updateMemoryRule",description:"Updates a memory rule. Validation runs against the rule as it would stand after the change, so a one-field update cannot pair a handler with a stored extractor override from the side.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-rules",httpMethod:"patch",pathParams:["memory_rule_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_rule_id","description":"Memory rule ID","required":true,"type":"string","in":"path"},{"name":"on","description":"The event a firing reads. `agents.generation.completed` fires once per completed turn whatever the transport, and is the only event the built-in extractor may bind to. `conversations.message.generated` fires per persisted assistant reply and is conversation-backed, so it is for custom handlers.","required":false,"type":"string","in":"body"},{"name":"source_agent_ids","description":"Send `null` to widen the rule to every agent in the project","required":false,"type":"array","in":"body"},{"name":"agent_id","description":"","required":false,"type":"string","in":"body"},{"name":"tool_id","description":"","required":false,"type":"string","in":"body"},{"name":"action","description":"","required":false,"type":"string","in":"body"},{"name":"preset_parameters","description":"","required":false,"type":"object","in":"body"},{"name":"prompt","description":"","required":false,"type":"string","in":"body"},{"name":"ai_provider_id","description":"","required":false,"type":"string","in":"body"},{"name":"model","description":"","required":false,"type":"string","in":"body"},{"name":"enabled","description":"","required":false,"type":"boolean","in":"body"}]},"delete-memory-rule":{serviceClass:"MemoryRules",operationId:"deleteMemoryRule",description:"Deletes a memory rule. The assertions it wrote are kept; their `rule_id` becomes null.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-rules",httpMethod:"delete",pathParams:["memory_rule_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_rule_id","description":"Memory rule ID","required":true,"type":"string","in":"path"}]},"list-memory-stores":{serviceClass:"MemoryStores",operationId:"listMemoryStores",description:"Returns a list of memory store configurations for a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"get",pathParams:[],queryParams:["project_id","tags","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"tags","description":"Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n","required":false,"type":"array","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-memory-store":{serviceClass:"MemoryStores",operationId:"createMemoryStore",description:"Creates a new memory store configuration in a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"name","description":"Memory store name","required":true,"type":"string","in":"body"},{"name":"description","description":"Optional description","required":false,"type":"string","in":"body"},{"name":"tags","description":"Optional key-value tags. Scopes the memory store in knowledge search, where every requested pair must match exactly.","required":false,"type":"object","in":"body"},{"name":"duplicate_threshold","description":"The store's dedup policy: cosine similarity at or above which an incoming fact is already known and the write is skipped. `null` (the default) uses the algorithm constant `0.95`. A single `POST /api/v1/memories` call may override it; the agent tool and the post-turn rule never can.","required":false,"type":"number","in":"body"},{"name":"supersede_threshold","description":"Cosine similarity at or above which an incoming fact restates a known one that has changed: the matched memory is invalidated and replaced. `null` (the default) uses the algorithm constant `0.90`. Must be lower than the effective `duplicate_threshold`, or the write is rejected with `400 VALIDATION_FAILED` — equal makes `superseded` unreachable and inverted swallows `skipped`.","required":false,"type":"number","in":"body"}]},"get-memory-store":{serviceClass:"MemoryStores",operationId:"getMemoryStore",description:"Returns a single memory store configuration by ID",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"get",pathParams:["memory_store_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"","required":true,"type":"string","in":"path"}]},"update-memory-store":{serviceClass:"MemoryStores",operationId:"updateMemoryStore",description:"Updates an existing memory store configuration",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"put",pathParams:["memory_store_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"","required":true,"type":"string","in":"path"},{"name":"name","description":"Memory store name","required":false,"type":"string","in":"body"},{"name":"description","description":"Optional description","required":false,"type":"string","in":"body"},{"name":"tags","description":"Optional key-value tags. Replaces the stored bag; `null` clears it.","required":false,"type":"object","in":"body"},{"name":"duplicate_threshold","description":"The store's dedup policy: cosine similarity at or above which an incoming fact is already known and the write is skipped. `null` (the default) uses the algorithm constant `0.95`. A single `POST /api/v1/memories` call may override it; the agent tool and the post-turn rule never can.","required":false,"type":"number","in":"body"},{"name":"supersede_threshold","description":"Cosine similarity at or above which an incoming fact restates a known one that has changed: the matched memory is invalidated and replaced. `null` (the default) uses the algorithm constant `0.90`. Must be lower than the effective `duplicate_threshold`, or the write is rejected with `400 VALIDATION_FAILED` — equal makes `superseded` unreachable and inverted swallows `skipped`.","required":false,"type":"number","in":"body"}]},"delete-memory-store":{serviceClass:"MemoryStores",operationId:"deleteMemoryStore",description:"Deletes a memory store configuration",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"delete",pathParams:["memory_store_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"","required":true,"type":"string","in":"path"}]},"list-memory-store-assertions":{serviceClass:"MemoryStores",operationId:"listMemoryStoreAssertions",description:"Returns the store's write ledger, newest first: one row per write attempt, skips included. This is how \"which write path is filling this store?\" is asked — a question no column on a memory row could answer, because a skipped write produced no memory at all.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"get",pathParams:["memory_store_id"],queryParams:["mechanism","outcome","generation_id","since","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"","required":true,"type":"string","in":"path"},{"name":"mechanism","description":"Only assertions that came through this door.","required":false,"type":"string","in":"query"},{"name":"outcome","description":"Only assertions that resolved this way.","required":false,"type":"string","in":"query"},{"name":"generation_id","description":"Only assertions made during this generation. A generation that does not exist matches nothing rather than widening to the whole store.","required":false,"type":"string","in":"query"},{"name":"since","description":"Only assertions recorded at or after this instant.","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"export-memories":{serviceClass:"MemoryStores",operationId:"exportMemories",description:"Streams one store's memories as newline-delimited JSON — one memory object per line, oldest first. Invalidated memories (retracted or superseded) are left out unless `include_invalidated` asks for them, so the file holds what the store currently asserts. The rows are the rows the listing returns for the same caller.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"get",pathParams:["memory_store_id"],queryParams:["include_invalidated","tags"],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"Memory store whose memories are exported","required":true,"type":"string","in":"path"},{"name":"include_invalidated","description":"Include memories that were retracted or superseded","required":false,"type":"boolean","in":"query"},{"name":"tags","description":"Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n","required":false,"type":"array","in":"query"}]},"get-memory-store-tags":{serviceClass:"MemoryStores",operationId:"getMemoryStoreTags",description:"Returns all tags attached to the memory store",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"get",pathParams:["memory_store_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"Memory store ID","required":true,"type":"string","in":"path"}]},"replace-memory-store-tags":{serviceClass:"MemoryStores",operationId:"replaceMemoryStoreTags",description:"Replaces all tags on the memory store with the provided tags (not merged)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"put",pathParams:["memory_store_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"Memory store ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"merge-memory-store-tags":{serviceClass:"MemoryStores",operationId:"mergeMemoryStoreTags",description:"Merges provided tags into the memory store's existing tags (existing tags are preserved unless overridden)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"patch",pathParams:["memory_store_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"Memory store ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"list-metadata-schemas":{serviceClass:"MetadataSchemas",operationId:"listMetadataSchemas",description:"Returns the declarations in scope, oldest first. `resource_type` narrows them to one governed resource.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/metadata-schemas",httpMethod:"get",pathParams:[],queryParams:["project_id","resource_type","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID (proj_ prefix)","required":false,"type":"string","in":"query"},{"name":"resource_type","description":"Return only the declarations governing this resource.","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-metadata-schema":{serviceClass:"MetadataSchemas",operationId:"createMetadataSchema",description:"Declares what `metadata` must satisfy for one resource type under one selector. A document is selected by `path_prefix`, matched on a path boundary: `/reports` covers `/reports/q1.txt` and never `/reports-archive/q1.txt`. The schema is compiled here, so one JSON Schema cannot parse is refused rather than stored — a stored one would be a rule that silently governs nothing. One selector has one schema per resource type; a second declaration of the same one is `409 NAME_CONFLICT`. The reserved root `/.system` cannot be governed: a platform-written document carries no caller metadata.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/metadata-schemas",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project the declaration governs. Optional for a project-scoped API key, which supplies it.","required":false,"type":"string","in":"body"},{"name":"resource_type","description":"The resource whose metadata this declaration governs. A type appears here once its write path reads the registry, so a declaration always has a door that enforces it.","required":true,"type":"string","in":"body"},{"name":"path_prefix","description":"The selector a `document` declaration must carry: the directory it governs.","required":false,"type":"string","in":"body"},{"name":"schema","description":"A JSON Schema. Its keywords are its own vocabulary and are stored as written.","required":true,"type":"object","in":"body"}]},"validate-metadata":{serviceClass:"MetadataSchemas",operationId:"validateMetadata",description:"Answers what a write would be told, without writing: a caller preparing a batch learns which declaration would refuse it, and why, before it sends anything. It reports; it does not enforce. The refusal itself lives in each resource's own write path, because a check a writer has to call is advisory and the writer who skips it is the one the rule exists for.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/metadata-schemas",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"","required":false,"type":"string","in":"body"},{"name":"path","description":"The path the document would be filed at, which decides which declaration governs it.","required":true,"type":"string","in":"body"},{"name":"metadata","description":"The bag to judge. Absent or `null` is judged as an empty bag.","required":false,"type":"object","in":"body"}]},"get-metadata-schema":{serviceClass:"MetadataSchemas",operationId:"getMetadataSchema",description:"Returns one declaration by id.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/metadata-schemas",httpMethod:"get",pathParams:["metadata_schema_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"metadata_schema_id","description":"","required":true,"type":"string","in":"path"}]},"update-metadata-schema":{serviceClass:"MetadataSchemas",operationId:"updateMetadataSchema",description:"Changes the schema, the selector, or both. `resource_type` is fixed at creation: it decides the selector's spelling and which write path reads the row, so changing it would silently repoint the declaration at a different door — delete it and declare again instead.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/metadata-schemas",httpMethod:"patch",pathParams:["metadata_schema_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"metadata_schema_id","description":"","required":true,"type":"string","in":"path"},{"name":"path_prefix","description":"The declaration's new selector.","required":false,"type":"string","in":"body"},{"name":"schema","description":"Replaces the declared JSON Schema.","required":false,"type":"object","in":"body"}]},"delete-metadata-schema":{serviceClass:"MetadataSchemas",operationId:"deleteMetadataSchema",description:"Removes the declaration. Documents already stored keep the metadata they hold — the rule governed writes, not rows.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/metadata-schemas",httpMethod:"delete",pathParams:["metadata_schema_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"metadata_schema_id","description":"","required":true,"type":"string","in":"path"}]},"list-model-routes":{serviceClass:"ModelRoutes",operationId:"listModelRoutes",description:"Returns the model routes defined in a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/model-routes",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-model-route":{serviceClass:"ModelRoutes",operationId:"createModelRoute",description:"Creates a project-scoped model route: a named, ordered list of provider+model targets tried in array order. Every target must reference an AI provider in the same project (400 otherwise), and the total attempt budget — the sum of `1 + max_retries` over all targets — may not exceed 10 (400 naming the computed total). A duplicate `name` in the project is rejected with 409.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/model-routes",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"name","description":"Human-readable name, unique per project","required":true,"type":"string","in":"body"},{"name":"targets","description":"Ordered failover targets. Position is priority: target 0 is tried first, and a retryable failure falls through to the next target.","required":true,"type":"array","in":"body"},{"name":"retry_on","description":"Which failure classes are failover-eligible. A failure whose class is not listed (and every deterministic failure — 400-class, auth, content policy) fails the generation immediately instead of spending another target's budget.","required":false,"type":"array","in":"body"},{"name":"failure_threshold","description":"Consecutive retryable failures after which a target is skipped for `cooldown_seconds`. Breaker state is in-process per node and keyed by (provider, model), so it is shared by every route pointing at the same backend.","required":false,"type":"integer","in":"body"},{"name":"cooldown_seconds","description":"How long a tripped target is skipped before being probed again","required":false,"type":"integer","in":"body"}]},"get-model-route":{serviceClass:"ModelRoutes",operationId:"getModelRoute",description:"Returns a specific model route",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/model-routes",httpMethod:"get",pathParams:["route_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"route_id","description":"Model route ID","required":true,"type":"string","in":"path"}]},"update-model-route":{serviceClass:"ModelRoutes",operationId:"updateModelRoute",description:"Updates a model route's name, targets, retry classes, or breaker configuration. Omitted fields are left unchanged.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/model-routes",httpMethod:"put",pathParams:["route_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"route_id","description":"Model route ID","required":true,"type":"string","in":"path"},{"name":"name","description":"New name (unique per project)","required":false,"type":"string","in":"body"},{"name":"targets","description":"Replacement target list (ordered)","required":false,"type":"array","in":"body"},{"name":"retry_on","description":"","required":false,"type":"array","in":"body"},{"name":"failure_threshold","description":"","required":false,"type":"integer","in":"body"},{"name":"cooldown_seconds","description":"","required":false,"type":"integer","in":"body"}]},"delete-model-route":{serviceClass:"ModelRoutes",operationId:"deleteModelRoute",description:"Deletes a model route. Returns 409 when an agent still references it — a routed agent has no pinned provider to fall back on, so the reference must be repointed or the agent deleted first.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/model-routes",httpMethod:"delete",pathParams:["route_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"route_id","description":"Model route ID","required":true,"type":"string","in":"path"}]},"list-orchestrations":{serviceClass:"Orchestrations",operationId:"listOrchestrations",description:"Returns orchestrations accessible to the caller.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Filter by project public ID","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-orchestration":{serviceClass:"Orchestrations",operationId:"createOrchestration",description:"Creates a new orchestration (pipeline) definition in the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project. Optional when authenticating with a project-scoped API key, which defaults to the key's project; required otherwise.","required":false,"type":"string","in":"body"},{"name":"name","description":"Human-readable name.","required":true,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"nodes","description":"The graph's nodes. Each `id` is unique and is what edges and `nodes.<id>` state references name; a node with no incoming edge starts the run.","required":true,"type":"array","in":"body"},{"name":"edges","description":"Directed edges between node `id`s. Every `from`/`to` must name a node in `nodes`, and the graph must be acyclic unless it contains a `loop` node. A node with several outgoing edges activates every target in parallel. `[]` for a graph of independent nodes.","required":true,"type":"array","in":"body"},{"name":"state_schema","description":"","required":false,"type":"object","in":"body"},{"name":"input_schema","description":"","required":false,"type":"object","in":"body"},{"name":"output_mapping","description":"The shape of a succeeded run's `output`, owned by the orchestration rather than by its terminal node ids. Each key is an output field (a dotted key such as `summary.title` builds a nested object); each value is JSON Logic (https://jsonlogic.com) evaluated against `{ \"state\": <final run state> }`, which includes `state.input` and `state.nodes.<id>`. A missing path maps to `null`; a mapping that throws fails the run. Null (or omitted) keys `output` by terminal node id. Versioned with the graph; `null` on update clears it.","required":false,"type":"object","in":"body"},{"name":"version_label","description":"Optional tag for the version this create archives, e.g. `initial`.","required":false,"type":"string","in":"body"}]},"validate-orchestration":{serviceClass:"Orchestrations",operationId:"validateOrchestration",description:"Statically validates an orchestration graph without persisting anything. Checks that every node has its required field, node ids are unique, edges reference existing nodes, the graph is acyclic (unless it contains a loop node), and every `input_mapping` `{\"var\": \"...\"}` reference resolves to a state key written by an upstream node or seeded by `input_schema`. Returns blocking `errors` and non-blocking `warnings` (e.g. a state key only written on a conditional branch). The same `errors` checks are enforced on create and update, which fail with `400` when any error is present.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"nodes","description":"The graph's nodes. Each `id` is unique and is what edges and `nodes.<id>` state references name; a node with no incoming edge starts the run. Checked exactly as create checks them, without persisting.","required":false,"type":"array","in":"body"},{"name":"edges","description":"Directed edges between node `id`s. Every `from`/`to` must name a node in `nodes`, and the graph must be acyclic unless it contains a `loop` node. A node with several outgoing edges activates every target in parallel. Checked exactly as create checks them, without persisting.","required":false,"type":"array","in":"body"},{"name":"input_schema","description":"Optional JSON Schema for run inputs; its top-level properties seed state.","required":false,"type":"object","in":"body"}]},"get-queue-stats":{serviceClass:"Orchestrations",operationId:"getQueueStats",description:"Returns a point-in-time snapshot of the orchestration run queue: how many tasks are waiting to be claimed (`queue_depth`), how many are currently claimed with a valid lease (`claimed_tasks`), the age of the oldest waiting task, recent claim-latency percentiles over a rolling in-process window. The per-project breakdown is listed by `GET /api/v1/orchestrations/queue/stats/projects`. Intended for admin/operator policies; guarded by `orchestrations:GetQueueStats`. Every figure is scoped to what the caller may see. A project-scoped caller gets `queue_depth` and `claimed_tasks` summed over their own projects, and `null` for `oldest_queued_age_seconds` and the `claim_latency_ms` percentiles — both describe the whole deployment and cannot be narrowed, so they are withheld rather than approximated. An unrestricted caller (the action granted on every project) gets the deployment-wide figures.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[]},"list-queue-stats-projects":{serviceClass:"Orchestrations",operationId:"listQueueStatsProjects",description:"One row per project with any queued or claimed task, ordered by project ID: the per-project breakdown of `GET /api/v1/orchestrations/queue/stats`. A project-scoped caller sees its own projects only. Guarded by `orchestrations:GetQueueStats`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:[],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-orchestration":{serviceClass:"Orchestrations",operationId:"getOrchestration",description:"Returns the orchestration with nodes and edges.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:["orchestration_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_id","description":"Public ID of the orchestration (orch_...)","required":true,"type":"string","in":"path"}]},"update-orchestration":{serviceClass:"Orchestrations",operationId:"updateOrchestration",description:"Partially updates an orchestration definition.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"patch",pathParams:["orchestration_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"orchestration_id","description":"Public ID of the orchestration (orch_...)","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"nodes","description":"The graph's nodes. Each `id` is unique and is what edges and `nodes.<id>` state references name; a node with no incoming edge starts the run. Replaces the stored list; omitted, the stored nodes are kept and validated against the new `edges`.","required":false,"type":"array","in":"body"},{"name":"edges","description":"Directed edges between node `id`s. Every `from`/`to` must name a node in `nodes`, and the graph must be acyclic unless it contains a `loop` node. A node with several outgoing edges activates every target in parallel. Replaces the stored list; omitted, the stored edges are kept and validated against the new `nodes`.","required":false,"type":"array","in":"body"},{"name":"state_schema","description":"","required":false,"type":"object","in":"body"},{"name":"input_schema","description":"","required":false,"type":"object","in":"body"},{"name":"output_mapping","description":"The shape of a succeeded run's `output`, owned by the orchestration rather than by its terminal node ids. Each key is an output field (a dotted key such as `summary.title` builds a nested object); each value is JSON Logic (https://jsonlogic.com) evaluated against `{ \"state\": <final run state> }`, which includes `state.input` and `state.nodes.<id>`. A missing path maps to `null`; a mapping that throws fails the run. Null (or omitted) keys `output` by terminal node id. Versioned with the graph; `null` on update clears it.","required":false,"type":"object","in":"body"},{"name":"version_label","description":"Optional tag for the version this write archives, e.g. `pre-rewire`. Ignored when the write changes no graph field, since no version is archived.","required":false,"type":"string","in":"body"},{"name":"expected_version","description":"Refuses the write unless the resource is at this version.","required":false,"type":"integer","in":"body"}]},"delete-orchestration":{serviceClass:"Orchestrations",operationId:"deleteOrchestration",description:"Deletes an orchestration definition and all its runs.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"delete",pathParams:["orchestration_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_id","description":"Public ID of the orchestration (orch_...)","required":true,"type":"string","in":"path"}]},"list-orchestration-versions":{serviceClass:"Orchestrations",operationId:"listOrchestrationVersions",description:"Returns the orchestration's archived graphs, newest first. A version is written on create and on every subsequent write that changes the graph (`nodes`, `edges`, `state_schema`, `input_schema`) — through the REST API or a formation apply alike. Metadata-only edits (name, description) do not archive a version. See [Versioning](/docs/modules/orchestrations#versioning).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:["orchestration_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_id","description":"Public ID of the orchestration (orch_...)","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-orchestration-version":{serviceClass:"Orchestrations",operationId:"getOrchestrationVersion",description:"Returns the exact graph a given version describes. Every run records the version it started on in `orchestration_version` and executes that graph for its whole life, so this is how you read the topology a run actually took — including a run whose orchestration has been rewired since.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:["orchestration_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_id","description":"Public ID of the orchestration (orch_...)","required":true,"type":"string","in":"path"},{"name":"version","description":"The archived version number","required":true,"type":"integer","in":"path"}]},"restore-orchestration-version":{serviceClass:"Orchestrations",operationId:"restoreOrchestrationVersion",description:"Writes an archived version's graph back as the orchestration's live definition, which archives it again as a **new** version rather than rewinding the counter — so a run pinned to any version in between still resolves the graph it started on. The restore runs through the ordinary update path, so the archived graph goes through the same static validation as an authored one. Node resource references (`agent_id`, `tool_id`, `orchestration_id`) resolve when a run reaches the node, so a target deleted since the snapshot was taken restores cleanly and surfaces as a failed run rather than a `400`. Restoring the graph the orchestration already holds is a no-op and archives nothing. Runs already in flight are unaffected either way — a restore is an ordinary edit, and pinning is what keeps it from reaching them.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"post",pathParams:["orchestration_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_id","description":"Public ID of the orchestration (orch_...)","required":true,"type":"string","in":"path"},{"name":"version","description":"The archived version number","required":true,"type":"integer","in":"path"},{"name":"label","description":"Optional tag for the version the restore creates. Defaults to `restored from v<version>`.","required":false,"type":"string","in":"body"}]},"list-orchestration-runs":{serviceClass:"Orchestrations",operationId:"listOrchestrationRuns",description:"Returns orchestration runs the caller can access, optionally filtered by orchestration, by parent run, by status, or by whether the run has a parent at all. Note when aggregating: a run's `usage` covers its whole subtree, so summing it over a list that contains both a parent and its children counts the children more than once. Pass `nested=false` to sum over runs a caller started.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:[],queryParams:["project_id","orchestration_id","parent_orchestration_run_id","nested","status","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Narrow the listing to one project (proj_...). A project-scoped key lists its own project without it.","required":false,"type":"string","in":"query"},{"name":"orchestration_id","description":"Filter by orchestration public ID (orch_...)","required":false,"type":"string","in":"query"},{"name":"parent_orchestration_run_id","description":"Filter to the runs one specific parent run's `loop` / `sub_orchestration` nodes started (run_...). This is how a caller holding a parent names the individual children behind its `usage`.","required":false,"type":"string","in":"query"},{"name":"nested","description":"Filter by whether the run was started by another run. `false` returns only the runs a caller started (no parent), which is the set to sum `usage` over; `true` returns only the runs a `loop` / `sub_orchestration` node started, across every parent. Omit to return both.\n\nContradicting `parent_orchestration_run_id` with `nested=false` is a `400`; any value other than `true` or `false` is a `400`.","required":false,"type":"boolean","in":"query"},{"name":"status","description":"Filter by run status. Repeat the parameter to OR values — `status=queued&status=running&status=sleeping&status=awaiting_input` is the set still driving, which is how a caller finds live work without paging every run the project ever started.\n\nThere is no `non_terminal` shorthand on purpose: which statuses count as live is the caller's policy. A value outside the enum, empty string included, is a `400`.","required":false,"type":"array","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"start-orchestration-run":{serviceClass:"Orchestrations",operationId:"startOrchestrationRun",description:"Creates a new run for the orchestration named by orchestration_id. By default the run executes durably in the background: the response returns immediately with status \"queued\" (a worker then claims it and moves it to \"running\") and progress is observed via get-orchestration-run or run lifecycle webhook events (orchestration_runs.started/awaiting_input/succeeded/failed). Delay and poll waits park the run as \"sleeping\" and are woken by a background scheduler, surviving restarts. Pass wait=true to block until the run reaches a terminal or awaiting_input state.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:["Idempotency-Key"],cookieParams:[],flags:[{"name":"Idempotency-Key","description":"The request's deduplication key. Equivalent to `idempotency_key` in the request body; sending both with different values is `400 VALIDATION_FAILED`. A non-empty string of at most 255 characters.","required":false,"type":"string","in":"header"},{"name":"orchestration_id","description":"Orchestration to run (orch_...).","required":true,"type":"string","in":"body"},{"name":"input","description":"Initial state for the run (merged with orchestration defaults).","required":false,"type":"object","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as `X-Soat-Context-<key>` headers on every `http`, `mcp` and `builtin` tool call made by an agent node of this run — including the agents of any child run a `loop` or `sub_orchestration` node starts. The header name is `X-Soat-Context-` plus the key verbatim; no character is re-cased.\n\nThe bag is stored on the run and re-read on every step, so it survives an `awaiting_input` pause, a `sleeping` wait, a background worker drive and a crash redrive. It is **write-only**: a run is a record every principal who may read runs can read, and a credential in it is not theirs to see, so it is never returned on one. It also does not outlive the run: reaching a terminal status (`succeeded`, `failed`, `cancelled` or `expired`) clears it. A key that is not a valid HTTP header name, or two keys that map to the same header, are rejected with `400 INVALID_TOOL_CONTEXT_KEY` and no run is created.\n\nThe reserved identity keys (`session_id`, `actor_id`, `actor_external_id`) are stripped at generation time — a caller cannot address them from here.","required":false,"type":"object","in":"body"},{"name":"metadata","description":"Caller-supplied key/value metadata attached to the run record for per-run attribution (e.g. which of your own tenants this run belongs to, or the dispatch batch that started it). Round-trips verbatim on every read of the run, on the list as well as the single read.\n\nThe bag is caller-owned and no key is reserved: server-owned state (status, the pinned orchestration version, the trace, usage, artifacts, the run's own `input` and accumulated `state`) lives in its own top-level field and cannot be written from here.\n\nIt is **not** merged into run state: no graph node sees it, and an `input_schema` never has to tolerate it — which is what makes it the place for an infrastructural label, rather than `input`. Keys are never transformed. It is not inherited by the child runs a `loop` or `sub_orchestration` node starts; each child carries whatever the graph gives it, which today is nothing.","required":false,"type":"object","in":"body"},{"name":"idempotency_key","description":"Deduplication key, unique within the project, that makes a retry of an ambiguous failure safe. The first request under a key starts the run and answers `201`; any later request carrying the same key answers `200` with that same run, whatever state it has reached — including a retry that arrives while the original is still running.\n\nThe key is claimed by the run record and stays claimed for as long as the record exists, so it never silently expires and lets a second run through.\n\n`orchestration_id`, `input`, `tool_context` and `metadata` are the request the key names: reusing a key with any of them changed is `409 IDEMPOTENCY_KEY_REUSED` rather than a replay of a run that does something else. `wait` is not part of that comparison — it says how the caller waits, not what the run is — so a retry may flip it.","required":false,"type":"string","in":"body"},{"name":"wait","description":"When true, block until the run reaches a terminal (succeeded/failed) or awaiting_input state and return the settled run. When false (default), return immediately with status \"queued\" and execute the run in the background.","required":false,"type":"boolean","in":"body"}]},"cancel-orchestration-run":{serviceClass:"Orchestrations",operationId:"cancelOrchestrationRun",description:"Cancels a run that has not yet reached a terminal state.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"post",pathParams:["orchestration_run_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_run_id","description":"Public ID of the run (run_...)","required":true,"type":"string","in":"path"}]},"pause-orchestration-run":{serviceClass:"Orchestrations",operationId:"pauseOrchestrationRun",description:"Parks a run in flight as `awaiting_input` at its next checkpoint, with a `required_action` of type `paused` naming the pause as operator-initiated rather than a node's. Unlike cancel, the run keeps its last checkpoint and resume-orchestration-run re-drives it from there — so work already done is deferred rather than discarded. A `queued` or `sleeping` run is parked immediately (a sleeping run keeps the wake it was due, and resuming hands it back to the scheduler at that instant). A `running` run keeps running until the round in flight reaches its checkpoint, so the response may still read `running` while `pause_requested_at` is set. A run already parked on a human, webhook or approval node keeps that node's `required_action`; the pause is still recorded, which is what makes submit-human-input refuse until the run is resumed. The pause fans out to the run's `loop` / `sub_orchestration` descendants — each parks at its own next checkpoint — because otherwise a parent's pause would bound nothing. Resuming does not fan out: each parked descendant is resumed by its own id. Idempotent: pausing an already-paused run answers with it unchanged.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"post",pathParams:["orchestration_run_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_run_id","description":"Public ID of the run (run_...)","required":true,"type":"string","in":"path"},{"name":"reason","description":"Why the run is being paused, surfaced on the parked run's `required_action.reason` and on `pause_reason`.","required":false,"type":"string","in":"body"}]},"submit-human-input":{serviceClass:"Orchestrations",operationId:"submitHumanInput",description:"Provides human input to a run that is awaiting_input at a human node.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"post",pathParams:["orchestration_run_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_run_id","description":"Public ID of the run (run_...)","required":true,"type":"string","in":"path"},{"name":"node_id","description":"ID of the human node to satisfy.","required":true,"type":"string","in":"body"},{"name":"output","description":"Output/response provided by the human reviewer.","required":false,"type":"object","in":"body"}]},"resume-orchestration-run":{serviceClass:"Orchestrations",operationId:"resumeOrchestrationRun",description:"Re-drives an awaiting_input orchestration run from its last checkpoint. This does not satisfy the pause itself — it carries no node_id or payload, so a run parked on a human or webhook-receive node re-parks on the same node. Use submit-human-input to supply the awaited payload and advance the run. It is also the only thing that lifts an operator pause (pause-orchestration-run): a run parked with `required_action.type` of `paused` re-drives the frontier that had not run yet, and one paused mid-timer goes back to `sleeping` for the wake it was already due.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"post",pathParams:["orchestration_run_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_run_id","description":"Public ID of the run (run_...)","required":true,"type":"string","in":"path"}]},"get-orchestration-run":{serviceClass:"Orchestrations",operationId:"getOrchestrationRun",description:"Returns the status, state, and artifacts of a specific run.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:["orchestration_run_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_run_id","description":"Public ID of the run (run_...)","required":true,"type":"string","in":"path"}]},"list-orchestration-run-node-executions":{serviceClass:"Orchestrations",operationId:"listOrchestrationRunNodeExecutions",description:"The run's per-node execution records, oldest first: one per node attempt, retries and skipped nodes included. Each captures the resolved input, output, status and error of that attempt — the orchestration analogue of an LLM trace. Reading them is reading the run (`orchestrations:GetRun`).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:["orchestration_run_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_run_id","description":"Public ID of the run (run_...)","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"list-policies":{serviceClass:"Policies",operationId:"listPolicies",description:"Returns global policies. Requires admin role. Pass user_id to list only the policies attached to that user.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/policies",httpMethod:"get",pathParams:[],queryParams:["user_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"user_id","description":"Return only policies attached to this user (user_...)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-policy":{serviceClass:"Policies",operationId:"createPolicy",description:"Creates a new global policy. Requires admin role.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/policies",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"document","description":"","required":true,"type":"object","in":"body"}]},"get-policy":{serviceClass:"Policies",operationId:"getPolicy",description:"Returns details of a specific policy. Requires admin role.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/policies",httpMethod:"get",pathParams:["policy_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"policy_id","description":"Policy public ID (pol_ prefix)","required":true,"type":"string","in":"path"}]},"update-policy":{serviceClass:"Policies",operationId:"updatePolicy",description:"Updates an existing global policy. Requires admin role.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/policies",httpMethod:"put",pathParams:["policy_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"policy_id","description":"Policy public ID (pol_ prefix)","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"document","description":"","required":true,"type":"object","in":"body"}]},"delete-policy":{serviceClass:"Policies",operationId:"deletePolicy",description:"Deletes a global policy. Requires admin role.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/policies",httpMethod:"delete",pathParams:["policy_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"policy_id","description":"Policy public ID (pol_ prefix)","required":true,"type":"string","in":"path"}]},"list-projects":{serviceClass:"Projects",operationId:"listProjects",description:"Lists projects accessible to the caller. - JWT admin: returns all projects. - JWT regular user: returns only projects granted by the user's policies. - API key scoped to a project: returns only that project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"get",pathParams:[],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-project":{serviceClass:"Projects",operationId:"createProject",description:"Creates a new project. Requires admin role.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"name","description":"","required":true,"type":"string","in":"body"}]},"get-project":{serviceClass:"Projects",operationId:"getProject",description:"Returns details of a specific project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"get",pathParams:["project_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID (proj_ prefix)","required":true,"type":"string","in":"path"}]},"update-project":{serviceClass:"Projects",operationId:"updateProject",description:"Updates a project's name, its attached guardrails (`guardrail_ids` — the project-scope baseline governing every tool call by every agent in the project), its orchestration concurrency limit (`max_concurrent_runs`), its continuation-chain ceiling (`max_chain_generations`), its orchestration nesting bound (`max_orchestration_run_depth`), its inherited model route (`default_model_route_id`), its read-auditing opt-in (`audit_reads_enabled`), its priced-model requirement (`require_priced_model`), its trace-content retention window (`trace_content_retention_days`), and/or its zero-retention setting (`trace_content_mode`). At least one field is required. Requires admin role. Detaching a guardrail (removing an id) additionally requires guardrails:DetachGuardrail.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"patch",pathParams:["project_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID (proj_ prefix)","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"guardrail_ids","description":"Guardrails attached at the project scope.","required":false,"type":"array","in":"body"},{"name":"max_concurrent_runs","description":"Maximum orchestration runs of this project driven at once. `null` clears the limit (unlimited); otherwise an integer >= 1. Enforced at queue claim time — excess runs stay queued until a slot frees.","required":false,"type":"integer","in":"body"},{"name":"max_chain_generations","description":"Generations one continuation chain in this project may hold before the platform stops resuming it. `null` clears the project's ceiling, leaving the deployment-wide `MAX_CONTINUATION_CHAIN_GENERATIONS`; otherwise an integer >= 1. The effective budget is the smallest of the deployment's ceiling, this one, and the agent's own `max_chain_generations` stop condition, so an agent author can be stricter than this number but never exceed it.","required":false,"type":"integer","in":"body"},{"name":"max_orchestration_run_depth","description":"`loop` / `sub_orchestration` nesting levels a run tree in this project may reach before the engine refuses to start the next child. `null` clears the project's bound, leaving the deployment-wide `MAX_ORCHESTRATION_RUN_DEPTH` (default 10); otherwise an integer >= 1. The effective bound is the smaller of the two, so this can only be stricter than the deployment's — which is what makes it a bound a graph author cannot opt out of. A refused child fails its parent run with `ORCHESTRATION_RUN_DEPTH_LIMIT`.","required":false,"type":"integer","in":"body"},{"name":"default_model_route_id","description":"Model route inherited by consumers in this project that bind neither `model_route_id` nor `ai_provider_id`. The route must belong to this project (`400` otherwise). `null` clears the default, which is refused with `409` while any consumer inherits it — repointing it to another route is always allowed and immediately changes which targets those consumers use.","required":false,"type":"string","in":"body"},{"name":"audit_reads_enabled","description":"Opt the project into read auditing. When true, `GET` requests that name this project are recorded in the audit log alongside mutations. Defaults to `false`.","required":false,"type":"boolean","in":"body"},{"name":"require_priced_model","description":"Refuse a generation whose model carries no price row for every billable token component, with `409 MODEL_NOT_PRICED`. Defaults to `false`, where an unpriced model runs and meters at `cost_usd: null`. The check runs before the provider is called, so a refused turn spends nothing, and covers every target of a model route the turn may fail over to.","required":false,"type":"boolean","in":"body"},{"name":"default_conversation_retrieval","description":"What a conversation that names no `retrieval` of its own does. `none` by default: a turn is embedded because someone asked for the conversation to be retrievable, never merely because it was said. Turns are stored and chunked on both settings.","required":false,"type":"string","in":"body"},{"name":"trace_content_retention_days","description":"How long trace and generation content is kept before the daily retention sweep content-purges it. `null` (the default) disables retention; otherwise an integer >= 1. The sweep uses the same purge path as `DELETE /traces/{trace_id}/content`, so the row survives as an auditable skeleton with `content_redacted_at` set.","required":false,"type":"integer","in":"body"},{"name":"trace_content_mode","description":"Whether trace and generation content is persisted at all. `full` (the default) stores it; `none` is zero-retention — content is never written, for every agent in the project. An agent may tighten this to `none` on its own but cannot loosen a `none` project back to `full`.","required":false,"type":"string","in":"body"}]},"delete-project":{serviceClass:"Projects",operationId:"deleteProject",description:"Deletes a project. Requires admin role. Fails with `409` if the project has any dependent resource, unless `force=true` is passed, in which case those resources are deleted along with the project. Every project-scoped resource counts: agents, ai providers, model routes, tools, ingestion rules, actors, chats, conversations, sessions, generations, traces, datasets, evals, workflows, tasks, triggers, orchestrations and their runs, formations, memory stores, secrets, files, guardrails, quotas, usage history — and the activity, approval, exception and guardrail-evaluation records a project accumulates while it runs. Audit log entries are the one exception: they outlive the project, keeping their record with `project_id` cleared.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"delete",pathParams:["project_id"],queryParams:["force"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID (proj_ prefix)","required":true,"type":"string","in":"path"},{"name":"force","description":"When `true`, deletes all of the project's dependent resources instead of returning `409 PROJECT_HAS_DEPENDENTS`.\n","required":false,"type":"boolean","in":"query"}]},"pause-project":{serviceClass:"Projects",operationId:"pauseProject",description:"Stops everything the project runs, with one call. From the moment it answers, nothing new starts in the project: agent, session and conversation generations, tool-output continuations, chat completions, direct tool calls, orchestration runs, eval runs and trigger firings are refused with `409 PROJECT_PAUSED`, and so is resuming a single run or task the pause holds. What is already in motion is parked rather than discarded: every live orchestration run is paused the way pause-orchestration-run pauses it (a running one parks at its next checkpoint), every open task's automation is paused, schedule triggers stop firing, and queued eval items wait unclaimed. A generation or node already in flight finishes. Reads and configuration writes keep working. Idempotent: pausing a paused project answers it unchanged and keeps the first `reason`. Emits `projects.paused`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"post",pathParams:["project_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID (proj_ prefix)","required":true,"type":"string","in":"path"},{"name":"reason","description":"Why the project is paused. Stored as `pause_reason` and carried onto every run and task the pause parks.","required":false,"type":"string","in":"body"}]},"resume-project":{serviceClass:"Projects",operationId:"resumeProject",description:"Lifts the project's pause and hands back exactly what it held: the runs and tasks the pause parked are resumed (a run the way resume-orchestration-run resumes it, in the background; a task's suppressed dispatch runs as the caller), and schedule triggers fire again from their next occurrence after now — an occurrence that fell due while paused is not fired late. A run or task an operator paused before the project was paused keeps its own pause. Eval runs continue from the item they stopped at. Emits `projects.resumed`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"post",pathParams:["project_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID (proj_ prefix)","required":true,"type":"string","in":"path"}]},"get-project-prices":{serviceClass:"Projects",operationId:"getProjectPrices",description:"Returns the project's per-provider-slug price rows — the middle pricing tier that covers every one of the project's instances of a given provider slug. At cost time a per-provider-instance override wins over these, and these win over the global default. Authorized by the caller's access to the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"get",pathParams:["project_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID (proj_ prefix)","required":true,"type":"string","in":"path"}]},"update-project-prices":{serviceClass:"Projects",operationId:"updateProjectPrices",description:"Upserts project + provider-slug price rows, keyed on (provider, model, effective_from). A row covers all of the project's instances of that provider slug. Authorized by the caller's access to the project. `effective_from` must be in the future once the (provider, model, component) is priced — past prices are immutable, so corrections ship as new future-dated rows. A first price, with no row for the project or the global default, may be dated now or earlier.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"put",pathParams:["project_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID (proj_ prefix)","required":true,"type":"string","in":"path"},{"name":"prices","description":"","required":true,"type":"array","in":"body"}]},"list-quotas":{serviceClass:"Quotas",operationId:"listQuotas",description:"Returns the quotas defined in a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/quotas",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-quota":{serviceClass:"Quotas",operationId:"createQuota",description:"Creates a project-scoped quota. `requests` is valid for `scope: project`/`api_key`; `tokens` and `cost_usd` are valid for `scope: project`/`agent`/`actor`; `storage_bytes` is valid for `scope: project` only. Any other scope/metric pair is rejected with 400 (no attribution exists to enforce it). An `actor` quota caps one end user's spend, matched from the generation's session; a null `scope_ref` means one budget *per* actor rather than a pooled project total. A `cost_usd` quota may name one `meter_type` to cap; omitting it caps every priced meter. A duplicate quota (same project, scope, scope_ref, metric, window, meter_type) is rejected with 409. `storage_bytes` caps a stored total rather than a windowed one, so it takes `window: current` and every other metric refuses that value (400 either way). It is enforced at the corpus write paths — file upload and create, document create, document ingest and re-ingest, memory create — with `409 QUOTA_STORAGE_EXCEEDED` and no `Retry-After`, since no window reset clears a footprint.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/quotas",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"scope","description":"The scope the quota applies to","required":true,"type":"string","in":"body"},{"name":"scope_ref","description":"Public id of the api key / agent / actor the quota applies to. For `api_key` and `agent` scope, NULL means all entities of that scope type in the project. For `actor` scope, NULL means one budget *per* actor — each end user gets their own allowance — rather than a pooled total across all actors.","required":false,"type":"string","in":"body"},{"name":"metric","description":"The metric being capped","required":true,"type":"string","in":"body"},{"name":"window","description":"The window over which the metric is aggregated. `current` is the only accepted value for storage_bytes (a stored total is not aggregated over time) and is refused for every other metric.","required":true,"type":"string","in":"body"},{"name":"limit","description":"The cap. Must be a positive integer for requests/tokens/storage_bytes (bytes); fractional values are allowed for cost_usd.","required":true,"type":"number","in":"body"},{"name":"mode","description":"enforce blocks with 429 (requests at the middleware, tokens/cost_usd at the pre-generation check); monitor observes without blocking — a breach fires the quota.exceeded webhook and writes a quotas:MonitorBreach audit entry, but the request is let through.","required":false,"type":"string","in":"body"},{"name":"on_unpriced","description":"Only for metric cost_usd (400 on any other metric). What an enforce quota does when the current window is a pricing blackout — several metered llm_tokens events, none of them priced, so the aggregate is 0 however much was actually spent. Platform meters such as compute_execution are read for the aggregate but never for this verdict. block (the default) refuses new generations with 409 QUOTA_UNENFORCEABLE until pricing is configured; allow accepts the unmeasurable spend explicitly. Either way a quota_unpriced exception is filed. monitor-mode quotas never block regardless. A partly priced window is not a blackout: no posture refuses it, it is enforced on its priced total, and it files the same exception.","required":false,"type":"string","in":"body"},{"name":"meter_type","description":"Only for metric cost_usd (400 on any other metric). The meter this cap answers for. Omit it and the cap sums every priced meter, which is the existing behaviour; name one and only that meter's cost counts, so an AI spend cap is not consumed by platform meters the operator prices (and vice versa). Part of the quota's identity, so two meter scopes can share a scope/metric/window and neither conflicts with an unscoped cap. Immutable after creation — replace the quota to change it.","required":false,"type":"string","in":"body"}]},"get-quota":{serviceClass:"Quotas",operationId:"getQuota",description:"Returns a specific quota, including current window usage",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/quotas",httpMethod:"get",pathParams:["quota_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"quota_id","description":"Quota ID","required":true,"type":"string","in":"path"}]},"update-quota":{serviceClass:"Quotas",operationId:"updateQuota",description:"Updates a quota's limit and/or mode. Other fields are immutable.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/quotas",httpMethod:"patch",pathParams:["quota_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"quota_id","description":"Quota ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"New limit","required":false,"type":"number","in":"body"},{"name":"mode","description":"New mode","required":false,"type":"string","in":"body"},{"name":"on_unpriced","description":"New pricing posture. Only for metric cost_usd (400 on any other metric); see the create operation for what block and allow mean.","required":false,"type":"string","in":"body"}]},"delete-quota":{serviceClass:"Quotas",operationId:"deleteQuota",description:"Deletes a quota and drops its window counters",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/quotas",httpMethod:"delete",pathParams:["quota_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"quota_id","description":"Quota ID","required":true,"type":"string","in":"path"}]},"list-secrets":{serviceClass:"Secrets",operationId:"listSecrets",description:"Returns a list of secrets for a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/secrets",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-secret":{serviceClass:"Secrets",operationId:"createSecret",description:"Creates a new encrypted secret in a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/secrets",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"name","description":"Secret name","required":true,"type":"string","in":"body"},{"name":"value","description":"Secret value (will be encrypted)","required":true,"type":"string","in":"body"}]},"get-secret":{serviceClass:"Secrets",operationId:"getSecret",description:"Returns a specific secret",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/secrets",httpMethod:"get",pathParams:["secret_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"secret_id","description":"Secret ID","required":true,"type":"string","in":"path"}]},"update-secret":{serviceClass:"Secrets",operationId:"updateSecret",description:"Updates a secret's name and/or value",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/secrets",httpMethod:"patch",pathParams:["secret_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"secret_id","description":"Secret ID","required":true,"type":"string","in":"path"},{"name":"name","description":"New secret name","required":false,"type":"string","in":"body"},{"name":"value","description":"New secret value","required":false,"type":"string","in":"body"}]},"delete-secret":{serviceClass:"Secrets",operationId:"deleteSecret",description:"Deletes a secret",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/secrets",httpMethod:"delete",pathParams:["secret_id"],queryParams:["force"],headerParams:[],cookieParams:[],flags:[{"name":"secret_id","description":"Secret ID","required":true,"type":"string","in":"path"},{"name":"force","description":"Delete the secret even when AI providers reference it, destroying those providers too. Without it a referenced secret answers SECRET_HAS_DEPENDENTS.","required":false,"type":"boolean","in":"query"}]},"list-sessions":{serviceClass:"Sessions",operationId:"listSessions",description:"Returns sessions the caller can access, optionally filtered by agent, actor and status.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"get",pathParams:[],queryParams:["project_id","agent_id","actor_id","status","tags","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Filter by project public ID. Required for `soat:ResourceTag` policy conditions to narrow the listing: a policy is read per project, so without it the listing is scoped to the projects the caller can access and nothing further.\n","required":false,"type":"string","in":"query"},{"name":"agent_id","description":"Filter by agent public ID","required":false,"type":"string","in":"query"},{"name":"actor_id","description":"Filter by actor public ID","required":false,"type":"string","in":"query"},{"name":"status","description":"Filter by session status (open, closed, or expired)","required":false,"type":"string","in":"query"},{"name":"tags","description":"Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n","required":false,"type":"array","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-session":{serviceClass:"Sessions",operationId:"createSession",description:"Creates a new session for the specified agent, along with the underlying conversation, so the caller only needs this single call to start interacting with the agent. No actor is created: pass `actor_id` to attach an existing actor as the session's end user. When it is omitted the session has no actor, and generations in it carry no end-user attribution — they are not billed to an actor in the usage meter and they match no `actor`-scoped quota.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent this session belongs to. With a credential scoped to a project, it may also be an agent another project shares with that project; the session is then that project's.\n","required":true,"type":"string","in":"body"},{"name":"name","description":"Optional session name","required":false,"type":"string","in":"body"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":false,"type":"object","in":"body"},{"name":"actor_id","description":"Optional public ID of an existing actor to use as the user actor. Actors are created separately (POST /actors); this field only links one. Omit it and the session has no end user, so its generations match no actor-scoped quota.\n","required":false,"type":"string","in":"body"},{"name":"auto_generate","description":"When true, automatically triggers generation after each user message.","required":false,"type":"boolean","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as `X-Soat-Context-<key>` headers on every `http`, `mcp` and `builtin` tool call in this session. The header name is the deployment's configured context prefix (`X-Soat-Context-` by default) plus the key verbatim — no character is re-cased. Keys are stored exactly as sent and never case-converted. A key that is not a valid HTTP header name, or two keys that map to the same header, are rejected with `400 INVALID_TOOL_CONTEXT_KEY`. `session_id`, `actor_id` and `actor_external_id` are server-derived and dropped from the stored bag, in any casing. Write-only: no read of a session returns it. It also does not outlive the session: closing or expiring it clears the bag.","required":false,"type":"object","in":"body"},{"name":"inactivity_ttl_seconds","description":"Number of seconds of inactivity after which the session expires. 0 means the session never expires.","required":false,"type":"integer","in":"body"},{"name":"message_delay_seconds","description":"Number of seconds to wait after the last user message before sending to the LLM. Acts as a debounce: each new message resets the timer. null or absent means no delay (immediate processing).\n","required":false,"type":"integer","in":"body"},{"name":"max_context_messages","description":"Maximum number of recent conversation messages sent to the model on each generation, taking precedence over the agent's `max_context_messages`. null or absent defers to the agent.\n","required":false,"type":"integer","in":"body"}]},"get-session":{serviceClass:"Sessions",operationId:"getSession",description:"Returns details of a single session, including a `usage` roll-up of what its generations cost. The listing omits `usage`; for a window, a split by day or model, or one end user across every session, narrow the usage aggregate with `session_id` / `actor_id` instead.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"get",pathParams:["session_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"}]},"update-session":{serviceClass:"Sessions",operationId:"updateSession",description:"Updates the session name and/or status.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"patch",pathParams:["session_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"},{"name":"name","description":"Session name (set to null to clear)","required":false,"type":"string","in":"body"},{"name":"status","description":"Session status","required":false,"type":"string","in":"body"},{"name":"auto_generate","description":"Enable or disable automatic generation after user messages.","required":false,"type":"boolean","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as `X-Soat-Context-<key>` headers on every `http`, `mcp` and `builtin` tool call in this session. The header name is the deployment's configured context prefix (`X-Soat-Context-` by default) plus the key verbatim — no character is re-cased. Keys are stored exactly as sent and never case-converted. A key that is not a valid HTTP header name, or two keys that map to the same header, are rejected with `400 INVALID_TOOL_CONTEXT_KEY`. `session_id`, `actor_id` and `actor_external_id` are server-derived and dropped from the stored bag, in any casing. Write-only: no read of a session returns it. It also does not outlive the session: closing or expiring it clears the bag.","required":false,"type":"object","in":"body"},{"name":"inactivity_ttl_seconds","description":"Number of seconds of inactivity after which the session expires. 0 means the session never expires. Updates the stored TTL; the inactivity clock continues from the last activity timestamp.\n","required":false,"type":"integer","in":"body"},{"name":"message_delay_seconds","description":"Number of seconds to wait after the last user message before sending to the LLM. Acts as a debounce: each new message resets the timer. Set to null to disable the delay.\n","required":false,"type":"integer","in":"body"},{"name":"max_context_messages","description":"Maximum number of recent conversation messages sent to the model on each generation, taking precedence over the agent's `max_context_messages`. Set to null to defer to the agent.\n","required":false,"type":"integer","in":"body"}]},"delete-session":{serviceClass:"Sessions",operationId:"deleteSession",description:"Deletes the session and its underlying conversation and messages. The session's actor is not deleted. Generations and traces produced by the session are not deleted either, since they are not linked to the session or conversation.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"delete",pathParams:["session_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"}]},"add-session-message":{serviceClass:"Sessions",operationId:"addSessionMessage",description:"Saves a user message to the session. When autoGenerate is enabled on the session and no generation is currently in progress, generation is triggered automatically and the response mirrors GenerateSessionResponse. Otherwise returns the saved user message.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"post",pathParams:["session_id"],queryParams:[],headerParams:["Idempotency-Key"],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"},{"name":"Idempotency-Key","description":"The request's deduplication key. Equivalent to `idempotency_key` in the request body; sending both with different values is `400 VALIDATION_FAILED`. A non-empty string of at most 255 characters.","required":false,"type":"string","in":"header"},{"name":"message","description":"User message text","required":false,"type":"string","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as `X-Soat-Context-<key>` headers on every `http`, `mcp` and `builtin` tool call in this generation. The header name is the deployment's configured context prefix (`X-Soat-Context-` by default) plus the key verbatim — no character is re-cased. Keys are never case-converted — they round-trip exactly as sent. An invalid or colliding key is rejected with `400 INVALID_TOOL_CONTEXT_KEY`.","required":false,"type":"object","in":"body"},{"name":"idempotency_key","description":"Optional deduplication key scoped to this session. If a message with the same key already exists in the session, the original message is returned with HTTP 200 and no new message or generation is triggered. Reusing the key with a different `message`, `document_id` or `tool_context` is `409 IDEMPOTENCY_KEY_REUSED`.\n","required":false,"type":"string","in":"body"},{"name":"document_id","description":"Public ID of a document used as the user message content.","required":false,"type":"string","in":"body"}]},"generate-session-response":{serviceClass:"Sessions",operationId:"generateSessionResponse",description:"Triggers the agent to generate a response based on the current conversation. Background by default: returns `202 Accepted` immediately while the generation runs. Pass ?wait=true to block and receive the assistant reply (or a requires_action status if the agent needs client tool outputs) in the response.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"post",pathParams:["session_id"],queryParams:["wait"],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"},{"name":"wait","description":"When omitted or `false` (default), generation runs in the background and `202 Accepted` is returned immediately. Pass `true` to block until the generation settles and receive the result. A tool call, `builtin` or MCP, always waits.","required":false,"type":"boolean","in":"query"},{"name":"model","description":"Optional model override","required":false,"type":"string","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as `X-Soat-Context-<key>` headers on every `http`, `mcp` and `builtin` tool call in this generation. The header name is the deployment's configured context prefix (`X-Soat-Context-` by default) plus the key verbatim — no character is re-cased. Keys are never case-converted — they round-trip exactly as sent. An invalid or colliding key is rejected with `400 INVALID_TOOL_CONTEXT_KEY`.","required":false,"type":"object","in":"body"}]},"submit-session-tool-outputs":{serviceClass:"Sessions",operationId:"submitSessionToolOutputs",description:"Submits client tool outputs for a generation that returned requires_action. The agent continues its loop and returns the final or next requires_action result.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"post",pathParams:["session_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"},{"name":"generation_id","description":"The generation ID from the requires_action response","required":true,"type":"string","in":"body"},{"name":"tool_outputs","description":"","required":true,"type":"array","in":"body"}]},"fork-session":{serviceClass:"Sessions",operationId:"forkSession",description:"Branches a new session from a point in this session's history: same context, different continuation. The fork gets its own conversation whose messages **reference the same documents** as the parent rather than copying them, so there is one stored copy of the content and a retention purge erases it from both. Recorded tool results ride along on those messages and are **replayed** as model input on the fork's next turn — forking never re-invokes a tool, so exploring a \"what if\" cannot send an email or charge a card a second time. The consequence to accept is that a forked turn sees the tool data as it was, not as it is now. The fork is created **inert**: `auto_generate` is false and no generation is triggered. Drive it with the normal message and generate endpoints. The fork has no actor — attach one only if the branch is meant to be driven by the same end user, since `single_session_per_actor` agents allow one open session per actor.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"post",pathParams:["session_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"},{"name":"fork_at_position","description":"The parent conversation `position` to branch after. Messages at positions 0..N are carried into the fork. Omit it to branch at the tip (the whole history).\n","required":false,"type":"integer","in":"body"},{"name":"agent_id","description":"Agent the fork runs against. Defaults to the parent session's agent; overriding it is the point of forking — same context, a different agent or agent version. Must belong to the same project as the session being forked.\n","required":false,"type":"string","in":"body"},{"name":"name","description":"Optional name for the forked session","required":false,"type":"string","in":"body"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":false,"type":"object","in":"body"},{"name":"tool_context","description":"Overrides the parent's `tool_context` on the fork. Omit it and the fork inherits the parent's, so the branch is faithful to the run it came from.\n","required":false,"type":"object","in":"body"}]},"list-session-forks":{serviceClass:"Sessions",operationId:"listSessionForks",description:"Returns the sessions forked directly from this one. One level of lineage: a fork of a fork is listed under its own parent.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"get",pathParams:["session_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-session-tags":{serviceClass:"Sessions",operationId:"getSessionTags",description:"Returns the session's tags object.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"get",pathParams:["session_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"}]},"replace-session-tags":{serviceClass:"Sessions",operationId:"replaceSessionTags",description:"Replaces all tags on the session.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"put",pathParams:["session_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"merge-session-tags":{serviceClass:"Sessions",operationId:"mergeSessionTags",description:"Merges the provided tags into the session's existing tags.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"patch",pathParams:["session_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"list-shares":{serviceClass:"Shares",operationId:"listShares",description:"With `role=publisher` (the default), the shares the project published. With `role=grantee`, the shares addressed to the project and the public shares it accepted, each with the project's own `acceptance`. A public share the project never accepted is not listed.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"get",pathParams:[],queryParams:["project_id","role","resource","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth, and always for `role=grantee`)","required":false,"type":"string","in":"query"},{"name":"role","description":"Which side of the shares to list","required":false,"type":"string","in":"query"},{"name":"resource","description":"Only shares of this resource SRN (`role=publisher` only)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-share":{serviceClass:"Shares",operationId:"createShare",description:"Offers one of the project's resources to another project — or, with `grantee: \"*\"`, to every project — with the listed actions. The share grants nothing until the grantee accepts it. `grantee: \"*\"` is refused with `403 PUBLIC_SHARES_DISABLED` unless the deployment sets `SHARES_ALLOW_PUBLIC=true`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"The publishing project (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"resource","description":"The SRN of one resource in the publishing project, `srn:<project_id>:<type>:<id>`. No wildcard.","required":true,"type":"string","in":"body"},{"name":"actions","description":"The actions granted: `tools:CallTool` for a `tool`, `agents:CreateAgentGeneration` for an `agent`. Any other action is `400 VALIDATION_FAILED`.","required":true,"type":"array","in":"body"},{"name":"grantee","description":"The project the share is offered to, or `*` for every project. Never the publishing project itself.","required":true,"type":"string","in":"body"},{"name":"cap","description":"The calls each accepting project may make through the share per fixed window; a call past it is `429 SHARE_CAP_EXCEEDED`. Omit or `null` for no cap.","required":false,"type":"object","in":"body"}]},"get-share":{serviceClass:"Shares",operationId:"getShare",description:"Returns a share with the grantee's view of the resource in `projection`. The publisher reads any of its shares; a grantee project, named by `project_id` or by its scoped credential, reads a share addressed to it or a public one, with its own `acceptance`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"get",pathParams:["share_id"],queryParams:["project_id"],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"},{"name":"project_id","description":"The project the caller reads as. Omit, or name the publisher, for the publisher's view; name a grantee project for its view.","required":false,"type":"string","in":"query"}]},"update-share":{serviceClass:"Shares",operationId:"updateShare",description:"Changes a share's `cap`, as its publisher. The next call through the share reads the new cap; a changed `window` starts a fresh count.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"patch",pathParams:["share_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"},{"name":"cap","description":"The calls each accepting project may make through the share per fixed window; a call past it is `429 SHARE_CAP_EXCEEDED`. `null` removes the cap.","required":false,"type":"object","in":"body"}]},"delete-share":{serviceClass:"Shares",operationId:"deleteShare",description:"Deletes a share and every acceptance of it. Each project whose acceptance was active gets a `share_revoked` activity entry.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"delete",pathParams:["share_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"}]},"accept-share":{serviceClass:"Shares",operationId:"acceptShare",description:"Records the grantee project's acceptance, which is what makes the share grant anything. Idempotent. A project that revoked its own acceptance may accept again; one whose acceptance the publisher revoked answers `403 SHARE_REVOKED` until the publisher deletes that acceptance. A suspended share can be accepted and grants nothing until resumed.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"post",pathParams:["share_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"},{"name":"project_id","description":"The accepting project (required if not using project key auth)","required":false,"type":"string","in":"body"}]},"suspend-share":{serviceClass:"Shares",operationId:"suspendShare",description:"Sets `suspended_at`, which denies every acceptance without removing any. Reversible with resume; nobody re-accepts. Each project with an active acceptance gets a `share_suspended` activity entry. Idempotent.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"post",pathParams:["share_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"}]},"resume-share":{serviceClass:"Shares",operationId:"resumeShare",description:"Clears `suspended_at`. Each project with an active acceptance gets a `share_resumed` activity entry. Idempotent.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"post",pathParams:["share_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"}]},"revoke-share":{serviceClass:"Shares",operationId:"revokeShare",description:"As the publisher, revokes the whole share and every acceptance of it; terminal. Each project whose acceptance was active gets a `share_revoked` activity entry. As a grantee (`project_id`, or a credential scoped to it), revokes that project's own acceptance, which it may accept again later; while the project's own resources still name the shared resource (see `GET /api/v1/shares/{share_id}/references`), that answers `409 SHARE_IN_USE` unless `force=true`. A publisher's revoke is never refused on that account. Idempotent.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"post",pathParams:["share_id"],queryParams:["force"],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"},{"name":"force","description":"As a grantee, revoke even while the project's resources name the shared resource; they degrade as for a revoked share.","required":false,"type":"boolean","in":"query"},{"name":"project_id","description":"The project the caller revokes for. Omit, or name the publisher, to revoke the share; name a grantee project to revoke its acceptance.","required":false,"type":"string","in":"body"}]},"list-share-references":{serviceClass:"Shares",operationId:"listShareReferences",description:"The grantee project's own resources that name the resource the share grants: agents binding a shared tool, pipelines stepping through it, ingestion rules converting with it, orchestrations, triggers and formations naming it. What degrades if the project revokes its acceptance, and what `409 SHARE_IN_USE` lists. The grantee is named by `project_id` or by its scoped credential; the publisher holds none of these and is answered `400`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"get",pathParams:["share_id"],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"},{"name":"project_id","description":"The grantee project whose references to list.","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"list-share-acceptances":{serviceClass:"Shares",operationId:"listShareAcceptances",description:"The projects that accepted the share, for its publisher.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"get",pathParams:["share_id"],queryParams:["status","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"},{"name":"status","description":"Only acceptances in this status","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"revoke-share-acceptance":{serviceClass:"Shares",operationId:"revokeShareAcceptance",description:"The publisher cutting one consumer. The acceptance is kept with `revoked_by: publisher`, and a new accept from that project answers `403 SHARE_REVOKED` until the publisher deletes it. The project gets a `share_revoked` activity entry if the acceptance was active.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"post",pathParams:["share_id","acceptance_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"},{"name":"acceptance_id","description":"Share acceptance ID","required":true,"type":"string","in":"path"}]},"delete-share-acceptance":{serviceClass:"Shares",operationId:"deleteShareAcceptance",description:"Removes one project's acceptance, which lets a project the publisher revoked accept again. The project gets a `share_revoked` activity entry if the acceptance was active.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"delete",pathParams:["share_id","acceptance_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"},{"name":"acceptance_id","description":"Share acceptance ID","required":true,"type":"string","in":"path"}]},"list-tasks":{serviceClass:"Tasks",operationId:"listTasks",description:"Lists tasks (the board query). Filter by workflow, state, status, automation status, or assignee — `GET /tasks?workflow_id=...&state=...` is one board column.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"get",pathParams:[],queryParams:["project_id","workflow_id","state","status","automation_status","assignee","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"","required":false,"type":"string","in":"query"},{"name":"workflow_id","description":"","required":false,"type":"string","in":"query"},{"name":"state","description":"","required":false,"type":"string","in":"query"},{"name":"status","description":"","required":false,"type":"string","in":"query"},{"name":"automation_status","description":"Filter by the current state's dispatch status. Repeat the parameter to OR values. `none` selects the tasks whose `automation_status` is `null` — the ones that never entered a state with an automation. It is a value a task really holds, so it is a value of the filter too; the parameter's own absence already means \"every task\".\n\nA value outside the enum, empty string included, is a `400`.","required":false,"type":"array","in":"query"},{"name":"assignee","description":"","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-task":{serviceClass:"Tasks",operationId:"createTask",description:"Creates a task bound to a workflow. By default the task is placed in the workflow's initial state; passing `state` places it directly in that named state instead — an alternate entry point for starting a task mid-flow (e.g. \"a new recorte for an existing theme by id\"), rather than re-submitting from the initial state and hoping a guard or similarity gate recognizes it. Entering the resulting state, initial or named, behaves identically: that state's `on_enter` automation fires and its `stalled_after` clock arms.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"","required":false,"type":"string","in":"body"},{"name":"workflow_id","description":"","required":true,"type":"string","in":"body"},{"name":"title","description":"","required":true,"type":"string","in":"body"},{"name":"payload","description":"","required":false,"type":"object","in":"body"},{"name":"assignee","description":"","required":false,"type":"string","in":"body"},{"name":"state","description":"Name of a declared workflow state to create the task in directly, instead of the workflow's `initial` state. Must name a state declared on the workflow, or the request is rejected with `TASK_STATE_NOT_FOUND` (400). Defaults to the `initial` state.","required":false,"type":"string","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as `X-Soat-Context-<key>` headers on every `http`, `mcp` and `builtin` tool call made by this task's automation dispatches — the agent generations a state's `on_enter` starts, and the agent nodes of any orchestration run it starts. The header name is `X-Soat-Context-` plus the key verbatim; no character is re-cased.\nCreation is the task's first move, so this is the bag the entry state's `on_enter` runs with. Each transition may replace it (see `TransitionTaskRequest.tool_context`).\nThe reserved identity keys (`session_id`, `actor_id`, `actor_external_id`) are stripped in any casing and re-derived server-side, so a task-dispatched generation cannot forge them. A key outside the HTTP header-name grammar is rejected with `INVALID_TOOL_CONTEXT_KEY` (400).\nWrite-only: the stored bag is never returned by any task read, and it is cleared when the task reaches a terminal state.","required":false,"type":"object","in":"body"},{"name":"metadata","description":"Caller-supplied key/value metadata attached to the task record for attribution — which of your own tenants the task belongs to, the ticket that raised it, the import batch that created it. Round-trips verbatim on every read of the task, the list included, and survives every transition (a transition supplies no metadata of its own).\n\nThe bag is caller-owned and no key is reserved: everything the engine decides about a task (`state`, `status`, `workflow_version`, `last_result`, `active_dispatch`, the automation fields) is a field of its own and cannot be written from here.\n\nPrefer this over `payload` for anything that is not task data: `payload` is read by every guard as `task.payload` and may be written by the workflow's declared `payload_writes`, so a label parked there is neither invisible to the state machine nor safe from it. A non-object is rejected with `400 VALIDATION_FAILED` and no task is created.","required":false,"type":"object","in":"body"}]},"get-task":{serviceClass:"Tasks",operationId:"getTask",description:"Retrieves a task, including its active dispatch and automation status.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"get",pathParams:["task_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"task_id","description":"","required":true,"type":"string","in":"path"}]},"update-task":{serviceClass:"Tasks",operationId:"updateTask",description:"Updates a task's payload, title, or assignee. `state` is never directly writable — move it with a transition; sending a `state` field is rejected as an unknown field (`VALIDATION_FAILED`). `payload` is shallow-merged over the existing payload (PATCH semantics): keys the request omits are preserved. The payload is caller-owned; the automation result lives in the read-only `last_result` field, which no patch can reach. The merged payload is validated against the workflow's `payload_schema`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"patch",pathParams:["task_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"task_id","description":"","required":true,"type":"string","in":"path"},{"name":"title","description":"","required":false,"type":"string","in":"body"},{"name":"payload","description":"Partial payload, shallow-merged over the existing payload. Omitted keys are preserved; provided keys overwrite. The merged result must satisfy the workflow's payload_schema.","required":false,"type":"object","in":"body"},{"name":"assignee","description":"","required":false,"type":"string","in":"body"}]},"delete-task":{serviceClass:"Tasks",operationId:"deleteTask",description:"Deletes a task. Its transition history cascades.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"delete",pathParams:["task_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"task_id","description":"","required":true,"type":"string","in":"path"}]},"transition-task":{serviceClass:"Tasks",operationId:"transitionTask",description:"Fires a named transition on a task. The transition must exist in the workflow and be valid from the task's current state; its guard must pass. This is the single path every state change routes through. A transition declaring `requires_approval` does not move the task — it parks a pending ApprovalItem and returns the task with `pending_transition` set; the move applies only when the approval is approved.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"post",pathParams:["task_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"task_id","description":"","required":true,"type":"string","in":"path"},{"name":"transition","description":"","required":true,"type":"string","in":"body"},{"name":"note","description":"","required":false,"type":"string","in":"body"},{"name":"tool_context","description":"Caller context for the automation dispatches the task makes from here on, forwarded as `X-Soat-Context-<key>` headers on their tool calls.\nSupplying it **replaces** the task's stored bag wholesale; omitting it keeps the current one, so the context follows whoever last moved the task and survives every move that does not speak about it — including an approval gate, a retry, and an automation hop. Send an empty object to clear it without closing the task.\nThe reserved identity keys (`session_id`, `actor_id`, `actor_external_id`) are stripped in any casing and re-derived server-side. A key outside the HTTP header-name grammar is rejected with `INVALID_TOOL_CONTEXT_KEY` (400).\nWrite-only: never returned by a task read, and cleared when the transition closes the task.","required":false,"type":"object","in":"body"}]},"pause-task":{serviceClass:"Tasks",operationId:"pauseTask",description:"Pauses a task's automation. While a pause is in force no state's `on_enter` dispatches and no retry chain continues — an agent generation, a tool call and a sub-orchestration alike — so the task stops spending without losing its place. A workflow has no run object, so this is the workflow half of pause-orchestration-run: the pause lands on the instance, which is the task. Transitions are deliberately still allowed — a move costs nothing while every dispatch it would start is suppressed — so a board stays usable under a pause. Entering a state whose dispatch is suppressed records `automation_status: paused`, which resume-task reads to know that state still owes its work. A dispatch already in flight is left to finish, and its outcome still routes; only what would start after it is suppressed. Idempotent: pausing an already-paused task answers with it unchanged.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"post",pathParams:["task_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"task_id","description":"","required":true,"type":"string","in":"path"},{"name":"reason","description":"Why the task is being paused, surfaced on `pause_reason`.","required":false,"type":"string","in":"body"}]},"resume-task":{serviceClass:"Tasks",operationId:"resumeTask",description:"Lifts a task's pause. When the pause suppressed the current state's `on_enter` — `automation_status: paused` — that dispatch is started now, as the caller resuming rather than as whoever last moved the task. A state whose dispatch had already completed, or that declares none, is left alone, so a resume never re-spends work the pause did not stop. This is the only way a pause is lifted; a task that is merely idle is advanced by firing a transition instead.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"post",pathParams:["task_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"task_id","description":"","required":true,"type":"string","in":"path"}]},"get-task-history":{serviceClass:"Tasks",operationId:"getTaskHistory",description:"Returns the append-only transition history of a task.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"get",pathParams:["task_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"task_id","description":"","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"list-tools":{serviceClass:"Tools",operationId:"listTools",description:"Returns all tools in the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tools",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID to filter by","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-tool":{serviceClass:"Tools",operationId:"createTool",description:"Creates a new tool in the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tools",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project","required":false,"type":"string","in":"body"},{"name":"name","description":"Tool name","required":true,"type":"string","in":"body"},{"name":"type","description":"Tool type (default http)","required":false,"type":"string","in":"body"},{"name":"description","description":"What the tool does","required":false,"type":"string","in":"body"},{"name":"parameters","description":"JSON Schema for tool input","required":false,"type":"object","in":"body"},{"name":"execute","description":"Execution config for http tools. Supported fields: `url` (required), `method` (default `POST`), `headers`, `body_mode` and `response_mode`. The `url` may contain `{paramName}` placeholders (e.g. `/users/{userId}`) that are replaced at call time with the corresponding tool argument value (URL-encoded). Arguments consumed as path parameters are excluded from the query string and request body. `body_mode` is `json` (default) or `multipart`. In `multipart` mode the merged tool arguments are sent as a `multipart/form-data` body: scalar fields become plain form fields and a field shaped like `{ content_type, filename, data_base64 }` is decoded from base64 and attached as a file part (the hardcoded `Content-Type: application/json` is dropped so `fetch` sets the multipart boundary itself). `response_mode` is `json` (default: the body parsed as JSON, else returned as text) or `base64`, which returns a 2xx body as `{ content_type, filename, data_base64 }` — the same file shape `multipart` accepts, so a pipeline step can relay a downloaded file. `filename` comes from `Content-Disposition` and is omitted when the target names none; a body over `TOOL_RESPONSE_MAX_BYTES` is `502 TOOL_RESPONSE_TOO_LARGE`.\n\n`auth` adds a computed request credential, for targets whose `Authorization` value cannot be expressed as a static header. Supported `auth.type` values:\n\n- `aws_sigv4` — signs the request with AWS Signature Version 4. Requires `region`, `service`, `access_key_id` and `secret_access_key`; `session_token` is optional (temporary credentials). Incompatible with `body_mode: multipart`, whose body bytes are not known at signing time.\n- `gcp_service_account` — mints a Google OAuth 2.0 access token from a signed service account assertion and sends it as a bearer token. Requires `credentials` (the service account key file JSON, as a string) and `scopes` (a non-empty array). Tokens are cached per service account and scope set until shortly before they expire.\n\nCredential fields accept `{{secret:...}}` references and should use them — a tool is readable by anyone who can `GET /tools`, and the stored reference is what is echoed back, never the resolved value.\n\nA credential written as a **literal** is stored and still sent on every call, but it is masked on every read: `secret_access_key`, `session_token` and `credentials` under `auth`, and any credential-named `headers` value (`Authorization`, `Cookie`, or a name containing `api-key`/`token`/`secret`/`password`), come back as `{\"no_echo\": true}`. The mask is an object rather than a string so a read-edit-write round trip fails the schema check instead of writing the placeholder in as the credential. A value carrying a `{{secret:...}}` reference is the wiring, not the credential, and stays readable.\n\n`headers` values additionally accept `{{context:<key>}}` references, resolved per call from the caller's `tool_context`, so a per-user credential can be placed in the real header the target expects (`Authorization: Bearer {{context:ocaToken}}`) instead of only in an `X-Soat-Context-<key>` header. Valid **only** inside `headers` — a context value is caller-supplied, so it may not steer the `url` — and a key missing from the `tool_context` at call time fails the tool call with `MISSING_TOOL_CONTEXT_KEY` rather than sending an empty credential. See the Tool Context reference.\n","required":false,"type":"object","in":"body"},{"name":"mcp","description":"MCP server config (`url`, `headers`). `headers` values accept `{{secret:...}}` and `{{context:<key>}}` references, resolved right before the outbound MCP request; `url` accepts `{{secret:...}}` only. A literal credential in `headers` is masked on read, exactly as in `execute`.","required":false,"type":"object","in":"body"},{"name":"actions","description":"Allowlist of actions. For `builtin` tools: SOAT platform action names (required). For `mcp` tools: an optional allowlist of MCP tool names to scope the server surface — omit or set `null` to expose every tool the MCP server offers. Ignored for other tool types.","required":false,"type":"array","in":"body"},{"name":"denied_actions","description":"For `mcp` tools: an optional denylist of MCP tool names to hide. Applied after `actions` and taking precedence over it. Use it to scope a read+write MCP server read-only by denying just the write tools. Omit or set `null` to deny nothing. Ignored for other tool types.","required":false,"type":"array","in":"body"},{"name":"context_keys","description":"Optional allowlist of `tool_context` keys that may be forwarded to this tool as prefixed context headers (`X-Soat-Context-<key>` by default). When `null` or omitted, every key in the caller's `tool_context` is forwarded. When set, only the listed keys are, so a per-user credential in `tool_context` can be confined to the tools that need it; `[]` forwards none. The server-pinned identity keys (`session_id`, `actor_id`, `actor_external_id`) are always forwarded. A key consumed by a `{{context:<key>}}` token in this tool's own headers is substituted regardless of this list — the tool declared that header itself.","required":false,"type":"array","in":"body"},{"name":"preset_parameters","description":"Fixed parameters pinned on every call this tool makes, whatever its type. Keys matching fields in the input schema are removed from the schema shown to the model, and a pinned value wins over one the model or a direct caller supplies for the same key.\n\nValues accept `{{context:<key>}}` references, resolved per call from the caller's `tool_context`, so a pin can be the run's own value — the one account this run may act on — rather than one fixed when the tool was created. A resolved value is retyped to the parameter's declared schema type; a key missing from the call's `tool_context` fails the call with `MISSING_TOOL_CONTEXT_KEY` rather than sending the literal placeholder. `{{secret:...}}` is not resolved here. See the Tool Context reference.","required":false,"type":"object","in":"body"},{"name":"pipeline","description":"Pipeline definition for `pipeline` tools. See the `pipeline` field on the Tool schema for the full structure.","required":false,"type":"object","in":"body"},{"name":"output_mapping","description":"Universal JSON Logic mapping applied to the tool's raw result. See the `output_mapping` field on the Tool schema for details.","required":false,"type":"object","in":"body"},{"name":"guardrail_ids","description":"Guardrails attached at the tool scope.","required":false,"type":"array","in":"body"}]},"get-tool":{serviceClass:"Tools",operationId:"getTool",description:"Returns a single tool by ID. A credential scoped to a project the tool is shared with, through an accepted share, reads its `id`, `name`, `description` and `parameters` only.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tools",httpMethod:"get",pathParams:["tool_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"tool_id","description":"","required":true,"type":"string","in":"path"}]},"update-tool":{serviceClass:"Tools",operationId:"updateTool",description:"Updates an existing tool.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tools",httpMethod:"patch",pathParams:["tool_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"tool_id","description":"","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"type","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"parameters","description":"","required":false,"type":"object","in":"body"},{"name":"execute","description":"Execution config for http tools. Supported fields: `url` (required), `method` (default `POST`), `headers`, `body_mode` and `response_mode`. The `url` may contain `{paramName}` placeholders (e.g. `/users/{userId}`) that are replaced at call time with the corresponding tool argument value (URL-encoded). Arguments consumed as path parameters are excluded from the query string and request body. `body_mode` is `json` (default) or `multipart`. In `multipart` mode the merged tool arguments are sent as a `multipart/form-data` body: scalar fields become plain form fields and a field shaped like `{ content_type, filename, data_base64 }` is decoded from base64 and attached as a file part (the hardcoded `Content-Type: application/json` is dropped so `fetch` sets the multipart boundary itself). `response_mode` is `json` (default: the body parsed as JSON, else returned as text) or `base64`, which returns a 2xx body as `{ content_type, filename, data_base64 }` — the same file shape `multipart` accepts, so a pipeline step can relay a downloaded file. `filename` comes from `Content-Disposition` and is omitted when the target names none; a body over `TOOL_RESPONSE_MAX_BYTES` is `502 TOOL_RESPONSE_TOO_LARGE`.\n\n`auth` adds a computed request credential, for targets whose `Authorization` value cannot be expressed as a static header. Supported `auth.type` values:\n\n- `aws_sigv4` — signs the request with AWS Signature Version 4. Requires `region`, `service`, `access_key_id` and `secret_access_key`; `session_token` is optional (temporary credentials). Incompatible with `body_mode: multipart`, whose body bytes are not known at signing time.\n- `gcp_service_account` — mints a Google OAuth 2.0 access token from a signed service account assertion and sends it as a bearer token. Requires `credentials` (the service account key file JSON, as a string) and `scopes` (a non-empty array). Tokens are cached per service account and scope set until shortly before they expire.\n\nCredential fields accept `{{secret:...}}` references and should use them — a tool is readable by anyone who can `GET /tools`, and the stored reference is what is echoed back, never the resolved value.\n\nA credential written as a **literal** is stored and still sent on every call, but it is masked on every read: `secret_access_key`, `session_token` and `credentials` under `auth`, and any credential-named `headers` value (`Authorization`, `Cookie`, or a name containing `api-key`/`token`/`secret`/`password`), come back as `{\"no_echo\": true}`. The mask is an object rather than a string so a read-edit-write round trip fails the schema check instead of writing the placeholder in as the credential. A value carrying a `{{secret:...}}` reference is the wiring, not the credential, and stays readable.\n\n`headers` values additionally accept `{{context:<key>}}` references, resolved per call from the caller's `tool_context`, so a per-user credential can be placed in the real header the target expects (`Authorization: Bearer {{context:ocaToken}}`) instead of only in an `X-Soat-Context-<key>` header. Valid **only** inside `headers` — a context value is caller-supplied, so it may not steer the `url` — and a key missing from the `tool_context` at call time fails the tool call with `MISSING_TOOL_CONTEXT_KEY` rather than sending an empty credential. See the Tool Context reference.\n","required":false,"type":"object","in":"body"},{"name":"mcp","description":"MCP server config (`url`, `headers`). `headers` values accept `{{secret:...}}` and `{{context:<key>}}` references, resolved right before the outbound MCP request; `url` accepts `{{secret:...}}` only. A literal credential in `headers` is masked on read, exactly as in `execute`.","required":false,"type":"object","in":"body"},{"name":"actions","description":"Allowlist of actions. For `builtin` tools: SOAT platform action names. For `mcp` tools: an optional allowlist of MCP tool names to scope the server surface (`null` exposes every tool). Ignored for other tool types.","required":false,"type":"array","in":"body"},{"name":"denied_actions","description":"For `mcp` tools: an optional denylist of MCP tool names to hide. Applied after `actions` and taking precedence over it. Use it to scope a read+write MCP server read-only by denying just the write tools. `null` denies nothing. Ignored for other tool types.","required":false,"type":"array","in":"body"},{"name":"context_keys","description":"Optional allowlist of `tool_context` keys that may be forwarded to this tool as prefixed context headers (`X-Soat-Context-<key>` by default). When `null` or omitted, every key in the caller's `tool_context` is forwarded. When set, only the listed keys are, so a per-user credential in `tool_context` can be confined to the tools that need it; `[]` forwards none. The server-pinned identity keys (`session_id`, `actor_id`, `actor_external_id`) are always forwarded. A key consumed by a `{{context:<key>}}` token in this tool's own headers is substituted regardless of this list — the tool declared that header itself.","required":false,"type":"array","in":"body"},{"name":"preset_parameters","description":"Fixed parameters pinned on every call this tool makes, whatever its type. Keys matching fields in the input schema are removed from the schema shown to the model, and a pinned value wins over one the model or a direct caller supplies for the same key.\n\nValues accept `{{context:<key>}}` references, resolved per call from the caller's `tool_context`, so a pin can be the run's own value — the one account this run may act on — rather than one fixed when the tool was created. A resolved value is retyped to the parameter's declared schema type; a key missing from the call's `tool_context` fails the call with `MISSING_TOOL_CONTEXT_KEY` rather than sending the literal placeholder. `{{secret:...}}` is not resolved here. See the Tool Context reference.","required":false,"type":"object","in":"body"},{"name":"pipeline","description":"Pipeline definition for `pipeline` tools. See the `pipeline` field on the Tool schema for the full structure.","required":false,"type":"object","in":"body"},{"name":"output_mapping","description":"Universal JSON Logic mapping applied to the tool's raw result. See the `output_mapping` field on the Tool schema for details.","required":false,"type":"object","in":"body"},{"name":"guardrail_ids","description":"Guardrails attached at the tool scope.","required":false,"type":"array","in":"body"}]},"delete-tool":{serviceClass:"Tools",operationId:"deleteTool",description:"Deletes a tool by ID. A tool that is a decider's backend is refused with `409 TOOL_HAS_DEPENDENTS`, and so is one another project has accepted a share of, or one of its own project's ingestion rules converts with, until `force=true`. Every share of the tool is revoked when it is deleted; an ingestion rule keeps its `tool_id` and fails the documents it matches with `CONVERTER_FAILED` until repointed.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tools",httpMethod:"delete",pathParams:["tool_id"],queryParams:["force"],headerParams:[],cookieParams:[],flags:[{"name":"tool_id","description":"","required":true,"type":"string","in":"path"},{"name":"force","description":"Delete the tool even when another project has accepted a share of it, revoking those shares, or an ingestion rule converts with it. A decider backend still refuses.","required":false,"type":"boolean","in":"query"}]},"call-tool":{serviceClass:"Tools",operationId:"callTool",description:"Directly invokes a tool and returns its output. Supported for `http`, `builtin`, `mcp`, and `pipeline` tools. `client` tools cannot be invoked server-side and will return 422. A `pipeline` tool runs its declared steps in order and returns the mapped `output` (or the last step's output); `action` is ignored and `input` is the pipeline input. For `builtin` and `mcp` tools the `action` field is required and identifies which action (SOAT) or tool name (MCP) to invoke. For `http` tools `action` is ignored. When a `builtin` or `mcp` tool declares an `actions` allowlist, an action outside it is rejected with `400 VALIDATION_FAILED` (\"not available on this tool\") before any outbound request is made. `preset_parameters` stored on the tool are pinned over the caller-supplied `input` before execution: a key the tool presets keeps its preset value even when `input` sets it. Keys the presets do not name are taken from `input` as sent. Guardrails attached to the tool or to its project adjudicate the call before dispatch, composing project + tool scope. A call this route cannot await a decision on — class C (human sign-off), class D, or a class-B tripwire — is refused with `422 TOOL_DISPATCH_FAILED`, whose `meta` carries the `tool_id` and the `outcome`. A `pipeline` tool is adjudicated before its first step runs, and every step is adjudicated as the call of that tool it is. A credential scoped to a project the tool is shared with, through an accepted share, calls it in that project: the calling project's guardrails adjudicate it, it is metered there with `publisher_project_id`, and the tool receives the calling project as the `calling_project_id` tool context key.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tools",httpMethod:"post",pathParams:["tool_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"tool_id","description":"","required":true,"type":"string","in":"path"},{"name":"action","description":"For `builtin` tools: the action name (must be in the tool's `actions` list). For `mcp` tools: the MCP tool name to invoke (must be in the tool's `actions` allowlist when one is set, and must not be in its `denied_actions` denylist). Ignored for `http` tools.\n","required":false,"type":"string","in":"body"},{"name":"input","description":"Input parameters for the tool call. These are merged with the tool's `preset_parameters` before execution; a preset value wins over the same key sent here.\n","required":false,"type":"object","in":"body"},{"name":"tool_context","description":"Key/value context for this call, forwarded to the tool as `X-Soat-Context-<key>` request headers and resolving any `{{context:<key>}}` token in the tool's `execute.headers`, `mcp.headers` or `preset_parameters`. Narrowed by the tool's `context_keys` allowlist when it sets one.\nThis route has no session, so it stamps no server-derived identity: the reserved keys `session_id`, `actor_id` and `actor_external_id` are dropped from this bag (in any casing) rather than forwarded, so a downstream tool can still trust that a context header naming one is server-derived. Every other key becomes an HTTP header name and must match that grammar, or the call fails with `INVALID_TOOL_CONTEXT_KEY`.\n","required":false,"type":"object","in":"body"}]},"list-traces":{serviceClass:"Traces",operationId:"listTraces",description:"Returns a paginated list of execution traces for the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/traces",httpMethod:"get",pathParams:[],queryParams:["project_id","parent_trace_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID to filter by","required":false,"type":"string","in":"query"},{"name":"parent_trace_id","description":"Only the direct children of this trace. A trace naming nothing the caller can read matches nothing rather than widening the listing.","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-trace":{serviceClass:"Traces",operationId:"getTrace",description:"Returns a single trace by ID.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/traces",httpMethod:"get",pathParams:["trace_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trace_id","description":"Public ID of the trace","required":true,"type":"string","in":"path"}]},"get-trace-tree":{serviceClass:"Traces",operationId:"getTraceTree",description:"Returns the full execution tree rooted at the given trace (or its root if the given trace is a child). Each node represents one agent's execution session. The `children` array contains traces triggered by sub-agent tool calls from that trace. A node's generations are listed by `GET /api/v1/generations?trace_id=`, and a node's direct children page by `GET /api/v1/traces?parent_trace_id=`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/traces",httpMethod:"get",pathParams:["trace_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trace_id","description":"Public ID of any trace in the tree (root or child)","required":true,"type":"string","in":"path"}]},"purge-trace-content":{serviceClass:"Traces",operationId:"purgeTraceContent",description:"Deletes the trace's steps object from storage and clears its content columns (`file_id`, `error`), cascading to every descendant trace and to all of their generations. A descendant holds its own steps object covering the same run, so the cascade is what makes the erasure complete rather than merely partial. The rows survive as auditable skeletons with `content_redacted_at` set — ids, timestamps, step counts, and the generations' usage-attribution fields are preserved, because the billing and audit ledger must outlive a tenant's erasure of the content. A purged trace therefore reads back as a skeleton, not a 404: a 404 would prove nothing. Idempotent — purging an already-purged trace succeeds and leaves the original `content_redacted_at` in place.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/traces",httpMethod:"delete",pathParams:["trace_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trace_id","description":"Public ID of the trace","required":true,"type":"string","in":"path"}]},"list-triggers":{serviceClass:"Triggers",operationId:"listTriggers",description:"Lists triggers. Filter by project, starter type, or target type.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"get",pathParams:[],queryParams:["project_id","type","target_type","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"","required":false,"type":"string","in":"query"},{"name":"type","description":"","required":false,"type":"string","in":"query"},{"name":"target_type","description":"","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-trigger":{serviceClass:"Triggers",operationId:"createTrigger",description:"Creates a new trigger for a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project. Optional when authenticating with a project-scoped API key, which defaults to the key's project; required otherwise.","required":false,"type":"string","in":"body"},{"name":"name","description":"","required":true,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"type","description":"","required":true,"type":"string","in":"body"},{"name":"target_type","description":"","required":true,"type":"string","in":"body"},{"name":"target_id","description":"","required":true,"type":"string","in":"body"},{"name":"action","description":"Tool targets only — the action for builtin/mcp tools","required":false,"type":"string","in":"body"},{"name":"input","description":"","required":false,"type":"object","in":"body"},{"name":"tool_context","description":"Caller context every firing forwards to the run it starts, so an agent whose tools authorize through `{{context:<key>}}` can be put on a schedule — a firing has no request to carry one. Each key is forwarded as one `X-Soat-Context-<key>` header.\n\n**Write-only.** It is accepted here and never returned on a read, so the record cannot be used to recover a value.\n\nA value may be a `{{secret:sec_...}}` reference, which keeps the credential in the [secret](/docs/modules/secrets) store and leaves only its id on the trigger; it is resolved at fire time, so rotating the secret changes the next firing without touching the trigger. A reference is refused here rather than at fire time when it is not in that id form (a secret's name does not resolve) or names a secret that does not exist in this project.\n","required":false,"type":"object","in":"body"},{"name":"cron","description":"5-field cron expression (UTC). Required when type is schedule","required":false,"type":"string","in":"body"},{"name":"event_pattern","description":"Internal-event subscription pattern. Required when type is event, rejected otherwise. `*` matches every event, `prefix.*` a namespace, or give an exact event name such as `documents.ingested`","required":false,"type":"string","in":"body"},{"name":"active","description":"","required":false,"type":"boolean","in":"body"},{"name":"policy_id","description":"","required":false,"type":"string","in":"body"}]},"get-trigger":{serviceClass:"Triggers",operationId:"getTrigger",description:"Retrieves the details of a specific trigger",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"get",pathParams:["trigger_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trigger_id","description":"","required":true,"type":"string","in":"path"}]},"update-trigger":{serviceClass:"Triggers",operationId:"updateTrigger",description:"Updates an existing trigger's configuration. The type is immutable. Changing `target_type` or `target_id` re-checks the target-start action (`orchestrations:StartRun`, `agents:CreateAgentGeneration` or `tools:CallTool`) against the resulting target, because a firing runs with the trigger creator's authority rather than the updater's. A caller who could not start the new target is answered `403` and the trigger keeps the target it had.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"patch",pathParams:["trigger_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trigger_id","description":"","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"target_type","description":"","required":false,"type":"string","in":"body"},{"name":"target_id","description":"","required":false,"type":"string","in":"body"},{"name":"action","description":"","required":false,"type":"string","in":"body"},{"name":"input","description":"","required":false,"type":"object","in":"body"},{"name":"tool_context","description":"Replaces the stored bag; `null` clears it. Write-only and resolved at fire time — see `CreateTriggerRequest.tool_context`.\n","required":false,"type":"object","in":"body"},{"name":"cron","description":"","required":false,"type":"string","in":"body"},{"name":"event_pattern","description":"","required":false,"type":"string","in":"body"},{"name":"active","description":"","required":false,"type":"boolean","in":"body"},{"name":"policy_id","description":"","required":false,"type":"string","in":"body"}]},"delete-trigger":{serviceClass:"Triggers",operationId:"deleteTrigger",description:"Deletes a trigger",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"delete",pathParams:["trigger_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trigger_id","description":"","required":true,"type":"string","in":"path"}]},"fire-trigger":{serviceClass:"Triggers",operationId:"fireTrigger",description:"Fires a trigger synchronously and returns the terminal firing record. The firing itself always settles here; an `eval` target's run is queued rather than executed inline, so the record names a `queued` run to poll.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"post",pathParams:["trigger_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trigger_id","description":"","required":true,"type":"string","in":"path"},{"name":"input","description":"Fire-time input, shallow-merged over the trigger's static input. For `eval` targets it may carry `agent_version` and `baseline_run_id`.","required":false,"type":"object","in":"body"},{"name":"tool_context","description":"Fire-time caller context, shallow-merged per key over the trigger's stored `tool_context`. A manual fire has a caller, so this is the one path that can supply a value without storing it.\n\nForwarded exactly as written: a `{{secret:...}}` reference is resolved only in the trigger's stored bag, never in one supplied here.\n","required":false,"type":"object","in":"body"}]},"get-trigger-secret":{serviceClass:"Triggers",operationId:"getTriggerSecret",description:"Retrieves the signing secret for a webhook trigger",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"get",pathParams:["trigger_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trigger_id","description":"","required":true,"type":"string","in":"path"}]},"rotate-trigger-secret":{serviceClass:"Triggers",operationId:"rotateTriggerSecret",description:"Rotates the signing secret for a webhook trigger",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"post",pathParams:["trigger_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trigger_id","description":"","required":true,"type":"string","in":"path"}]},"list-trigger-firings":{serviceClass:"Triggers",operationId:"listTriggerFirings",description:"Lists firings for a trigger (trigger_id is required).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"get",pathParams:[],queryParams:["trigger_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"trigger_id","description":"Trigger to list firings for (trg_...)","required":true,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-trigger-firing":{serviceClass:"Triggers",operationId:"getTriggerFiring",description:"Retrieves the details of a specific trigger firing",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"get",pathParams:["firing_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"firing_id","description":"","required":true,"type":"string","in":"path"}]},"list-usage-events":{serviceClass:"Usage",operationId:"listUsageEvents",description:"Returns the raw usage events the caller can access, most recent first, narrowed by any combination of the same filters the aggregate takes — agent, generation, trace, actor, session, AI provider, orchestration run, orchestration, tool, trigger, action, outcome, meter type, model or `source` — so a rollup and the events behind it are addressed the same way. Each event is the per-generation token usage as reported by the provider, for audit and reconciliation. An id filter matches the id the event recorded, so it still selects the spend of an agent, provider, session or tool since deleted. An id no event in scope recorded yields an empty page rather than dropping the filter, so a mistyped narrowing can never widen the list past what was asked for.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/usage",httpMethod:"get",pathParams:[],queryParams:["agent_id","generation_id","trace_id","actor_id","session_id","ai_provider_id","orchestration_run_id","orchestration_id","model","trigger_id","action_id","tool_id","publisher_project_id","outcome","meter_type","source","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Filter by agent public ID","required":false,"type":"string","in":"query"},{"name":"generation_id","description":"Filter by generation public ID","required":false,"type":"string","in":"query"},{"name":"trace_id","description":"Filter by trace public ID","required":false,"type":"string","in":"query"},{"name":"actor_id","description":"Filter by the actor (end user) the usage is attributed to.\n","required":false,"type":"string","in":"query"},{"name":"session_id","description":"Filter by the session the usage was produced in.\n","required":false,"type":"string","in":"query"},{"name":"ai_provider_id","description":"Filter by the provider record the spend was billed against.\n","required":false,"type":"string","in":"query"},{"name":"orchestration_run_id","description":"Filter by the orchestration run that metered the event.\n","required":false,"type":"string","in":"query"},{"name":"orchestration_id","description":"Filter by orchestration — the events of the runs it started itself, not the subtree its `loop` or `sub_orchestration` nodes started. An orchestration that does not exist in scope yields an empty page.\n","required":false,"type":"string","in":"query"},{"name":"model","description":"Filter by the as-billed SKU the event recorded. Matched as recorded, so it still selects events whose provider has since been deleted.\n","required":false,"type":"string","in":"query"},{"name":"trigger_id","description":"Filter by the trigger that initiated the generation","required":false,"type":"string","in":"query"},{"name":"action_id","description":"Filter by logical action id","required":false,"type":"string","in":"query"},{"name":"tool_id","description":"Filter by the tool a `tool_execution` event metered, including a tool another project shares with one in scope.\n","required":false,"type":"string","in":"query"},{"name":"publisher_project_id","description":"Filter to the calls made through a share of a resource the named project owns.\n","required":false,"type":"string","in":"query"},{"name":"outcome","description":"Filter by how a `tool_execution` call ended: `ok`, `error` or `timeout`. Every other meter carries no outcome and is matched by none of them.\n","required":false,"type":"string","in":"query"},{"name":"meter_type","description":"Filter by meter type (e.g. `llm_tokens`, `compute_execution`, `api_request`, `storage`, `tool_execution`)\n","required":false,"type":"string","in":"query"},{"name":"source","description":"Filter by what the spend was incurred for. `eval` is an eval run's item generations, `eval_judge` an `llm_judge` scorer's own completion and `eval_scorer` a `tool` scorer's call, so verification spend is `source` in (`eval`, `eval_judge`, `eval_scorer`); `decider` is a decision's evaluation; `embedding` is any embedding call. Ordinary agent traffic carries no source and is matched by none of them.\n","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-usage-aggregate":{serviceClass:"Usage",operationId:"getUsageAggregate",description:"Returns a project's usage rolled up over an optional `[from, to]` time window, optionally bucketed by a single dimension and narrowed by any combination of sixteen filters — session, actor, agent, AI provider, orchestration run, orchestration, generation, trace, tool, publisher project, meter type, model, source, trigger, action and outcome. That is how \"what did this conversation cost\", \"what has this end user cost\" and \"what did this agent cost\" are answered. Each group and the grand total carry an `event_count`, summed token counts, a measured `quantity` per component (so infra meters report their real amount, not zeros), and `cost_usd` (null when no event in the bucket was priced). This is the per-project cost-by-range/by-category query — a monthly figure without scanning raw usage events client-side. Narrowings intersect and apply to the whole rollup — every bucket, `totals` and `totals.distinct` alike — so each composes with any `group_by`, and all sixteen are echoed back under `filters`. The ten naming a resource match the id the event recorded, so a deleted resource's spend stays selectable; an id no event recorded empties the rollup rather than being dropped: a mistyped id must never read back as the project's whole spend. `orchestration_id` is the exception — the event records no orchestration, so a deleted one empties the rollup. The other six are matched against the value the event recorded. `groups` is paginated. Its `total` is the number of distinct buckets in the window — bucket cardinality, including a null bucket for the events the dimension does not apply to, and never a count of entities. To count entities, send `include=distinct` and read `totals.distinct`. `totals` always describes the whole window — never the page above it, and never the bucketing: omit `group_by` and `totals` is unchanged while `groups` is empty.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/usage",httpMethod:"get",pathParams:[],queryParams:["project_id","group_by","include","from","to","meter_type","session_id","actor_id","agent_id","ai_provider_id","orchestration_run_id","orchestration_id","generation_id","trace_id","model","source","trigger_id","action_id","tool_id","publisher_project_id","outcome","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID to aggregate usage for","required":true,"type":"string","in":"query"},{"name":"group_by","description":"Dimension to bucket by. Omit it to skip bucketing entirely: `totals` still describes the whole window and `groups` is an empty page, which is what \"what did this one thing cost\" wants. A value naming no dimension is a `400`. `day` buckets on the event's UTC calendar day; the others bucket on the matching column. `ai_provider` buckets on the provider the spend was billed against — a routed generation's serving target, or the agent's pinned provider. `model` buckets on the model id *and* the provider that served it, so one model name served by two providers is two groups (see `ai_provider_id`). `source` buckets by what the spend was incurred for (`eval`, `eval_judge`), which is how verification spend is priced apart from the traffic serving real users; unlabelled traffic collapses into the single `null` bucket. `orchestration_run` buckets on the orchestration run the event belongs to — a standalone generation runs no orchestration and collapses into the single `null` bucket, so the bucket count under this dimension is not a count of runs.\n","required":false,"type":"string","in":"query"},{"name":"include","description":"Optional extras to compute. The only value is `distinct`, which adds `totals.distinct` — one distinct-entity count per attribution column. It is opt-in because each key costs the database one extra sort of the window, so the default response keeps its cost whatever the event table grows to carry; a caller counting entities sends one request at `limit=1&include=distinct`. Any other value is a `400`.\n","required":false,"type":"string","in":"query"},{"name":"from","description":"Inclusive lower bound (ISO-8601 timestamp) on the event created_at. Omit for no lower bound.\n","required":false,"type":"string","in":"query"},{"name":"to","description":"Inclusive upper bound (ISO-8601 timestamp) on the event created_at. Omit for no upper bound.\n","required":false,"type":"string","in":"query"},{"name":"meter_type","description":"Narrow the rollup to one meter type (e.g. `llm_tokens`, `compute_execution`, `api_request`, `storage`, `tool_execution`). Omit to include every meter. An unknown type yields an empty rollup, not an error.\n","required":false,"type":"string","in":"query"},{"name":"session_id","description":"Narrow the whole rollup — every bucket, `totals` and `totals.distinct` alike — to one session's traffic. Combines with `group_by`: `group_by=day&session_id=…` is a conversation's spend per day, `group_by=model` is the same spend split by model. Like every id narrowing it matches the id the event recorded, so a deleted session's spend stays selectable; an id no event in this project recorded yields an empty rollup, never the project total.\n","required":false,"type":"string","in":"query"},{"name":"actor_id","description":"The same narrowing, for one end user across every session they appear in. This is the figure behind a `scope: actor`, `metric: cost_usd` quota — what that actor has spent, against the cap it is held to.\n","required":false,"type":"string","in":"query"},{"name":"agent_id","description":"Narrow to one agent's traffic, across every session, run and trigger that dispatched it.\n","required":false,"type":"string","in":"query"},{"name":"ai_provider_id","description":"Narrow to the spend billed against one provider record — a routed generation's serving target, or the agent's pinned provider.\n","required":false,"type":"string","in":"query"},{"name":"orchestration_run_id","description":"Narrow to the events one orchestration run metered.","required":false,"type":"string","in":"query"},{"name":"orchestration_id","description":"Narrow to the runs of one orchestration — the runs it started itself, never the subtree a `loop` or `sub_orchestration` node started under it, which is metered against the child orchestration where it was incurred. Additive: summed across a project's orchestrations the figures reach the project total exactly once. For the subtree figure of a single invocation, read `usage` on the run.\n","required":false,"type":"string","in":"query"},{"name":"generation_id","description":"Narrow to one generation's events. A generation writes more than one when it meters several dimensions (tokens and compute).\n","required":false,"type":"string","in":"query"},{"name":"trace_id","description":"Narrow to the events recorded under one trace.","required":false,"type":"string","in":"query"},{"name":"model","description":"Narrow to one model, matched against the as-billed SKU the event recorded. A value naming no model yields an empty rollup, not an error. Under `group_by=model` this is a single-bucket rollup; under any other dimension it is that model's spend split by the dimension.\n","required":false,"type":"string","in":"query"},{"name":"source","description":"Narrow by what the spend was incurred for — `eval` and `eval_judge` are verification spend, `decider` is decision spend. Ordinary traffic carries no source, so it cannot be selected by this filter; omit the filter for everything.\n","required":false,"type":"string","in":"query"},{"name":"trigger_id","description":"Narrow to the spend one trigger initiated. Matched as recorded rather than resolved, so it still selects the spend of a trigger that has since been deleted.\n","required":false,"type":"string","in":"query"},{"name":"action_id","description":"Narrow to one caller-supplied action label, for per-action spend. Matched as recorded, like `trigger_id`.\n","required":false,"type":"string","in":"query"},{"name":"tool_id","description":"Narrow to the executions of one tool, including one another project shares with this one.\n","required":false,"type":"string","in":"query"},{"name":"publisher_project_id","description":"Narrow to the calls made through a share of a resource the named project owns.\n","required":false,"type":"string","in":"query"},{"name":"outcome","description":"Narrow `tool_execution` events by how the call ended — `ok`, `error` or `timeout` — matched as recorded. `outcome=error` is a tool's failures without a second meter.\n","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"list-usage-thresholds":{serviceClass:"Usage",operationId:"listUsageThresholds",description:"Lists the usage alert thresholds the caller can access, optionally filtered by project_id. Each threshold fires the `usage.threshold_crossed` webhook when a project's cost or token usage over a calendar-month or rolling-24h window crosses the configured value.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/usage",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Filter by project public ID","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-usage-threshold":{serviceClass:"Usage",operationId:"createUsageThreshold",description:"Creates a usage alert threshold on a project. Thresholds are immutable apart from deletion — to change one, delete and recreate it (which resets its fire state).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/usage",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"","required":true,"type":"string","in":"body"},{"name":"metric","description":"","required":true,"type":"string","in":"body"},{"name":"window","description":"","required":true,"type":"string","in":"body"},{"name":"threshold","description":"Must be greater than 0","required":true,"type":"number","in":"body"}]},"delete-usage-threshold":{serviceClass:"Usage",operationId:"deleteUsageThreshold",description:"Deletes a usage threshold, resetting its fire state. Recreating a threshold starts its once-per-window / hysteresis state fresh.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/usage",httpMethod:"delete",pathParams:["threshold_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"threshold_id","description":"Threshold public ID","required":true,"type":"string","in":"path"}]},"get-usage-receipt":{serviceClass:"Usage",operationId:"getUsageReceipt",description:"Returns a billing receipt. Pass generation_id for a per-generation receipt, or orchestration_run_id for a receipt summed across the orchestration run's events — both share the same shape (per-model line items with tokens, the price-book version that priced them, and cost, plus totals). Exactly one of generation_id or orchestration_run_id must be supplied.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/usage",httpMethod:"get",pathParams:[],queryParams:["generation_id","orchestration_run_id"],headerParams:[],cookieParams:[],flags:[{"name":"generation_id","description":"Generation public ID. Mutually exclusive with orchestration_run_id.\n","required":false,"type":"string","in":"query"},{"name":"orchestration_run_id","description":"Orchestration run public ID. Returns the receipt summed across every generation the run metered. Mutually exclusive with generation_id.\n","required":false,"type":"string","in":"query"}]},"get-price-book":{serviceClass:"Usage",operationId:"getPriceBook",description:"Returns the global price book — the versioned per-provider/model unit prices used to compute usage cost at write time. Readable by any authenticated user.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/usage",httpMethod:"get",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[]},"upsert-price-book":{serviceClass:"Usage",operationId:"upsertPriceBook",description:"Upserts price rows keyed on (provider, model, effective_from). Admin only. `effective_from` must be in the future once the (provider, model, component) is priced — past prices are immutable so recorded costs stay explainable; ship corrections as new future-dated rows. A first price, with no row in this scope or any broader one, may be dated now or earlier.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/usage",httpMethod:"put",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"prices","description":"","required":true,"type":"array","in":"body"}]},"get-current-user":{serviceClass:"Users",operationId:"getCurrentUser",description:"Returns the profile of the currently authenticated user",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/users",httpMethod:"get",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[]},"list-users":{serviceClass:"Users",operationId:"listUsers",description:"Returns a list of all users",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/users",httpMethod:"get",pathParams:[],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-user":{serviceClass:"Users",operationId:"createUser",description:"Creates a new user in the system",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/users",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"username","description":"","required":true,"type":"string","in":"body"},{"name":"password","description":"","required":true,"type":"string","in":"body"},{"name":"role","description":"","required":false,"type":"string","in":"body"}]},"get-user":{serviceClass:"Users",operationId:"getUser",description:"Returns the data of a specific user",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/users",httpMethod:"get",pathParams:["user_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"user_id","description":"User ID","required":true,"type":"string","in":"path"}]},"delete-user":{serviceClass:"Users",operationId:"deleteUser",description:"Deletes a specific user",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/users",httpMethod:"delete",pathParams:["user_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"user_id","description":"User ID","required":true,"type":"string","in":"path"}]},"bootstrap-user":{serviceClass:"Users",operationId:"bootstrapUser",description:"Creates the first admin user. Returns 409 if any user already exists.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/users",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"username","description":"","required":true,"type":"string","in":"body"},{"name":"password","description":"","required":true,"type":"string","in":"body"}]},"login-user":{serviceClass:"Users",operationId:"loginUser",description:"Authenticates a user and returns a JWT token",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/users",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"username","description":"","required":true,"type":"string","in":"body"},{"name":"password","description":"","required":true,"type":"string","in":"body"}]},"attach-user-policies":{serviceClass:"Users",operationId:"attachUserPolicies",description:"Replaces the user's policy list with the provided policy IDs. Requires admin role.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/users",httpMethod:"put",pathParams:["user_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"user_id","description":"User public ID (user_ prefix)","required":true,"type":"string","in":"path"},{"name":"policy_ids","description":"List of policy public IDs to attach (replaces existing)","required":true,"type":"array","in":"body"}]},"list-webhooks":{serviceClass:"Webhooks",operationId:"listWebhooks",description:"Lists webhooks. Use the project_id query parameter to filter by project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-webhook":{serviceClass:"Webhooks",operationId:"createWebhook",description:"Creates a new webhook for a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project. Optional when authenticating with a project-scoped API key, which defaults to the key's project; required otherwise.","required":false,"type":"string","in":"body"},{"name":"name","description":"","required":true,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"url","description":"","required":true,"type":"string","in":"body"},{"name":"events","description":"","required":true,"type":"array","in":"body"},{"name":"policy_id","description":"","required":false,"type":"string","in":"body"}]},"get-webhook":{serviceClass:"Webhooks",operationId:"getWebhook",description:"Retrieves the details of a specific webhook",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"get",pathParams:["webhook_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"webhook_id","description":"","required":true,"type":"string","in":"path"}]},"update-webhook":{serviceClass:"Webhooks",operationId:"updateWebhook",description:"Updates an existing webhook's configuration",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"put",pathParams:["webhook_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"webhook_id","description":"","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"url","description":"","required":false,"type":"string","in":"body"},{"name":"events","description":"","required":false,"type":"array","in":"body"},{"name":"active","description":"","required":false,"type":"boolean","in":"body"},{"name":"policy_id","description":"","required":false,"type":"string","in":"body"}]},"delete-webhook":{serviceClass:"Webhooks",operationId:"deleteWebhook",description:"Deletes a webhook and stops all event deliveries",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"delete",pathParams:["webhook_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"webhook_id","description":"","required":true,"type":"string","in":"path"}]},"list-webhook-deliveries":{serviceClass:"Webhooks",operationId:"listWebhookDeliveries",description:"Lists event deliveries for a webhook (webhook_id is required).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"get",pathParams:[],queryParams:["webhook_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"webhook_id","description":"Webhook to list deliveries for (wh_...)","required":true,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-webhook-delivery":{serviceClass:"Webhooks",operationId:"getWebhookDelivery",description:"Retrieves the details of a specific webhook delivery",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"get",pathParams:["delivery_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"delivery_id","description":"","required":true,"type":"string","in":"path"}]},"redeliver-webhook-delivery":{serviceClass:"Webhooks",operationId:"redeliverWebhookDelivery",description:"Queues the stored payload of an existing delivery to be sent again. A new delivery record is created rather than the original being reset, so the original attempt stays in the history. The send happens in the background: poll the returned delivery to observe its outcome.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"post",pathParams:["delivery_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"delivery_id","description":"Delivery to send again (wh_deliv_...)","required":true,"type":"string","in":"path"}]},"get-webhook-secret":{serviceClass:"Webhooks",operationId:"getWebhookSecret",description:"Retrieves the signing secret for the specified webhook",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"get",pathParams:["webhook_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"webhook_id","description":"","required":true,"type":"string","in":"path"}]},"rotate-webhook-secret":{serviceClass:"Webhooks",operationId:"rotateWebhookSecret",description:"Rotates the secret key for the specified webhook",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"post",pathParams:["webhook_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"webhook_id","description":"","required":true,"type":"string","in":"path"}]},"list-workflows":{serviceClass:"Workflows",operationId:"listWorkflows",description:"Lists workflow definitions in a project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/workflows",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-workflow":{serviceClass:"Workflows",operationId:"createWorkflow",description:"Creates a new workflow definition. The definition is statically validated.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/workflows",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"","required":false,"type":"string","in":"body"},{"name":"name","description":"","required":true,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"states","description":"","required":true,"type":"array","in":"body"},{"name":"transitions","description":"","required":true,"type":"array","in":"body"},{"name":"payload_schema","description":"","required":false,"type":"object","in":"body"},{"name":"version_label","description":"Optional tag for the version this create archives, e.g. `initial`.","required":false,"type":"string","in":"body"}]},"get-workflow":{serviceClass:"Workflows",operationId:"getWorkflow",description:"Retrieves a workflow definition.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/workflows",httpMethod:"get",pathParams:["workflow_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"workflow_id","description":"","required":true,"type":"string","in":"path"}]},"update-workflow":{serviceClass:"Workflows",operationId:"updateWorkflow",description:"Updates a workflow definition. Structural changes (states/transitions) are re-validated. Existing tasks in a removed state stay put but can only leave via transitions valid in the new definition.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/workflows",httpMethod:"patch",pathParams:["workflow_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"workflow_id","description":"","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"states","description":"","required":false,"type":"array","in":"body"},{"name":"transitions","description":"","required":false,"type":"array","in":"body"},{"name":"payload_schema","description":"","required":false,"type":"object","in":"body"},{"name":"version_label","description":"Optional tag for the version this write archives, e.g. `pre-rewire`. Ignored when the write changes no definition field, since no version is archived.","required":false,"type":"string","in":"body"},{"name":"expected_version","description":"Refuses the write unless the resource is at this version.","required":false,"type":"integer","in":"body"}]},"delete-workflow":{serviceClass:"Workflows",operationId:"deleteWorkflow",description:"Deletes a workflow. Rejected while open tasks exist.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/workflows",httpMethod:"delete",pathParams:["workflow_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"workflow_id","description":"","required":true,"type":"string","in":"path"}]},"list-workflow-versions":{serviceClass:"Workflows",operationId:"listWorkflowVersions",description:"Returns the workflow's archived state machines, newest first. A version is written on create and on every subsequent write that changes the definition (`states`, `transitions`, `payload_schema`) — through the REST API or a formation apply alike. Metadata-only edits (name, description) do not archive a version. See [Versioning](/docs/modules/workflows#versioning).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/workflows",httpMethod:"get",pathParams:["workflow_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"workflow_id","description":"","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-workflow-version":{serviceClass:"Workflows",operationId:"getWorkflowVersion",description:"Returns the exact state machine a given version describes. Every task records the version it entered on in `workflow_version` and runs on that machine for its whole life, so this is how you read the definition a task is actually being validated against — including a task whose workflow has been rewired since.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/workflows",httpMethod:"get",pathParams:["workflow_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"workflow_id","description":"","required":true,"type":"string","in":"path"},{"name":"version","description":"The archived version number","required":true,"type":"integer","in":"path"}]},"restore-workflow-version":{serviceClass:"Workflows",operationId:"restoreWorkflowVersion",description:"Writes an archived version's state machine back as the workflow's live definition, which archives it again as a **new** version rather than rewinding the counter — so a task pinned to any version in between still runs on the machine it entered on. The restore runs through the ordinary update path, so the archived definition goes through the same validation as an authored one. That includes resolving every `on_enter` dispatch target, so restoring a version whose agent or orchestration has since been deleted fails with `400` rather than writing a definition that would strand a task on entry. Restoring the definition the workflow already holds is a no-op and archives nothing. Tasks already in flight are unaffected either way — a restore is an ordinary edit, and pinning is what keeps it from reaching them.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/workflows",httpMethod:"post",pathParams:["workflow_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"workflow_id","description":"","required":true,"type":"string","in":"path"},{"name":"version","description":"The archived version number","required":true,"type":"integer","in":"path"},{"name":"label","description":"Optional tag for the new version the restore archives. Defaults to `restored from vN`.","required":false,"type":"string","in":"body"}]}};//#endregion
|
|
55
|
+
var routes={"list-activity":{serviceClass:"Activity",operationId:"listActivity",description:"Returns activity entries for a project, newest first, filterable by kind, severity, agent, generation and orchestration run — every filter composable with the rest. Paginated with an opaque cursor rather than offset/limit — pass the previous page's `next_cursor` to fetch the next one; a `null` `next_cursor` means there is no more data.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/activity",httpMethod:"get",pathParams:[],queryParams:["project_id","kind","severity","agent_id","generation_id","orchestration_run_id","cursor","limit"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"kind","description":"Filter by activity kind","required":false,"type":"string","in":"query"},{"name":"severity","description":"Filter by severity","required":false,"type":"string","in":"query"},{"name":"agent_id","description":"Filter to entries produced by one agent. Composable with every other filter — entries carry one agent, one generation and one run, so naming two narrows to the rows where both hold.","required":false,"type":"string","in":"query"},{"name":"generation_id","description":"Filter to entries produced during one agent generation. This is what turns `tool_resolution_failed` from alertable into usable: the warning for a suspect turn is one query rather than a scan of the project's feed.","required":false,"type":"string","in":"query"},{"name":"orchestration_run_id","description":"Filter to entries produced during one orchestration run.","required":false,"type":"string","in":"query"},{"name":"cursor","description":"Opaque cursor from a previous page's `next_cursor`","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"export-activity":{serviceClass:"Activity",operationId:"exportActivity",description:"Streams a project's activity entries as newline-delimited JSON — one entry object per line, **oldest first**, where the feed itself reads newest first: a feed answers what just happened, and a file is read start to end. `project_id` is required: the export is per-project by design. Filters behave exactly as they do on the list endpoint.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/activity",httpMethod:"get",pathParams:[],queryParams:["project_id","kind","severity","agent_id","generation_id","orchestration_run_id"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project whose activity is exported","required":true,"type":"string","in":"query"},{"name":"kind","description":"Only entries of this kind","required":false,"type":"string","in":"query"},{"name":"severity","description":"Only entries of this severity","required":false,"type":"string","in":"query"},{"name":"agent_id","description":"Only entries an agent produced","required":false,"type":"string","in":"query"},{"name":"generation_id","description":"Only entries a generation produced","required":false,"type":"string","in":"query"},{"name":"orchestration_run_id","description":"Only entries an orchestration run produced","required":false,"type":"string","in":"query"}]},"list-actors":{serviceClass:"Actors",operationId:"listActors",description:"Returns all actors the caller has access to. If projectId is provided, returns only actors in that project. project keys are scoped to a single project automatically. JWT users without projectId receive actors across all their accessible projects.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/actors",httpMethod:"get",pathParams:[],queryParams:["project_id","external_id","name","agent_id","chat_id","conversation_id","tags","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (optional)","required":false,"type":"string","in":"query"},{"name":"external_id","description":"External ID to filter by (e.g. WhatsApp phone number)","required":false,"type":"string","in":"query"},{"name":"name","description":"Case-insensitive substring match on the actor name","required":false,"type":"string","in":"query"},{"name":"agent_id","description":"Return only actors linked to this agent","required":false,"type":"string","in":"query"},{"name":"chat_id","description":"Return only actors linked to this chat","required":false,"type":"string","in":"query"},{"name":"conversation_id","description":"Return only actors that participate in this conversation (derived from the conversation's messages).\n","required":false,"type":"string","in":"query"},{"name":"tags","description":"Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n","required":false,"type":"array","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-actor":{serviceClass:"Actors",operationId:"createActor",description:"Creates a new actor. project keys automatically infer the project from the key's scope; JWT callers must supply projectId.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/actors",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID. Required for JWT auth; omit when using an project key.","required":false,"type":"string","in":"body"},{"name":"name","description":"","required":true,"type":"string","in":"body"},{"name":"external_id","description":"Optional external identifier (e.g. WhatsApp phone number). If provided and an actor with this externalId already exists in the project, the existing actor is returned (idempotent — 200 OK).","required":false,"type":"string","in":"body"},{"name":"instructions","description":"Persona-specific instructions composed into the effective system prompt during conversation generation.","required":false,"type":"string","in":"body"},{"name":"agent_id","description":"Agent to link this actor to. Mutually exclusive with chat_id.","required":false,"type":"string","in":"body"},{"name":"chat_id","description":"Chat to link this actor to. Mutually exclusive with agent_id.","required":false,"type":"string","in":"body"}]},"get-actor":{serviceClass:"Actors",operationId:"getActor",description:"Returns an actor by its ID",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/actors",httpMethod:"get",pathParams:["actor_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"actor_id","description":"Actor ID","required":true,"type":"string","in":"path"}]},"update-actor":{serviceClass:"Actors",operationId:"updateActor",description:"Updates an actor's properties",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/actors",httpMethod:"patch",pathParams:["actor_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"actor_id","description":"Actor ID","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"external_id","description":"External identifier (e.g. WhatsApp phone number)","required":false,"type":"string","in":"body"},{"name":"instructions","description":"Persona-specific instructions","required":false,"type":"string","in":"body"},{"name":"agent_id","description":"Agent to link this actor to. Mutually exclusive with chat_id.","required":false,"type":"string","in":"body"},{"name":"chat_id","description":"Chat to link this actor to. Mutually exclusive with agent_id.","required":false,"type":"string","in":"body"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":false,"type":"object","in":"body"}]},"delete-actor":{serviceClass:"Actors",operationId:"deleteActor",description:"Deletes an actor by its ID",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/actors",httpMethod:"delete",pathParams:["actor_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"actor_id","description":"Actor ID","required":true,"type":"string","in":"path"}]},"get-actor-tags":{serviceClass:"Actors",operationId:"getActorTags",description:"Returns all tags attached to the actor",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/actors",httpMethod:"get",pathParams:["actor_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"actor_id","description":"Actor ID","required":true,"type":"string","in":"path"}]},"replace-actor-tags":{serviceClass:"Actors",operationId:"replaceActorTags",description:"Replaces all tags on the actor with the provided tags (not merged)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/actors",httpMethod:"put",pathParams:["actor_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"actor_id","description":"Actor ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"merge-actor-tags":{serviceClass:"Actors",operationId:"mergeActorTags",description:"Merges provided tags into the actor's existing tags (existing tags are preserved unless overridden)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/actors",httpMethod:"patch",pathParams:["actor_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"actor_id","description":"Actor ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"list-agents":{serviceClass:"Agents",operationId:"listAgents",description:"Returns all agents in the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID to filter by","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-agent":{serviceClass:"Agents",operationId:"createAgent",description:"Creates a new agent bound to an AI provider.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project","required":false,"type":"string","in":"body"},{"name":"ai_provider_id","description":"Public ID of the AI provider to pin. Mutually exclusive with `model_route_id`.","required":false,"type":"string","in":"body"},{"name":"model_route_id","description":"Public ID of a model route in the same project. The agent's completion model is then resolved through the route's ordered targets with failover. Mutually exclusive with `ai_provider_id` and `model`.","required":false,"type":"string","in":"body"},{"name":"name","description":"Display name.","required":false,"type":"string","in":"body"},{"name":"instructions","description":"System instructions, sent as the system message of every generation.","required":false,"type":"string","in":"body"},{"name":"model","description":"Model identifier on the pinned AI provider. Omitted, the provider's default model is used. Cannot be combined with `model_route_id`.","required":false,"type":"string","in":"body"},{"name":"tool_bindings","description":"Tools to attach, one binding object per tool — the only attachment field. An entry is either a reference (`{ \"tool_id\": … }`) or an inline definition (`{ \"tool\": … }`). See [Tool Bindings](/docs/modules/agents#tool-bindings).","required":false,"type":"array","in":"body"},{"name":"max_steps","description":"Maximum agent loop steps per turn (default `20`).","required":false,"type":"integer","in":"body"},{"name":"tool_choice","description":"Tool choice strategy. Accepts a string (`\"auto\"`, `\"required\"`) or an object (`{ \"type\": \"tool\", \"tool_name\": \"my_tool\" }`). A forcing value (`\"required\"` or the object form) forbids a final assistant message on every step of every turn, including a resumed or continued one, so it requires a `has_tool_call` entry in `stop_conditions` — otherwise the write is refused with `FORCED_TOOL_CHOICE_CANNOT_STOP`.","required":false,"type":"unknown","in":"body"},{"name":"stop_conditions","description":"Conditions that end the agent's work early, on top of `max_steps`. Two scopes:\n\n`{\"type\": \"has_tool_call\", \"tool_name\": \"<resolved tool name>\"}` ends the **turn** after the step that calls the named tool. It narrows when the loop ends — it never lets it run past `max_steps`.\n\n`{\"type\": \"max_chain_generations\", \"max_generations\": <n>}` bounds the **continuation chain** instead: once the chain has spawned that many generations, further resumptions stop with `chain_limit` rather than extending it. It never shortens a turn. The effective ceiling is the smaller of this and the deployment's `MAX_CONTINUATION_CHAIN_GENERATIONS`, so an agent can be stricter than the platform but never looser.\n\nAn unknown `type`, a `has_tool_call` without a `tool_name`, a `max_chain_generations` whose `max_generations` is not a positive integer, or a non-object entry is rejected with 400.","required":false,"type":"array","in":"body"},{"name":"active_tool_ids","description":"Persisted tools from `tool_bindings` the model sees on every step. Omitted or `[]` leaves every bound tool active. See [Active Tools](/docs/modules/agents#active-tools).","required":false,"type":"array","in":"body"},{"name":"guardrail_ids","description":"Guardrails attached at the agent scope.","required":false,"type":"array","in":"body"},{"name":"step_rules","description":"Per-step overrides of `tool_choice` and `active_tool_ids`, numbered from the first step of the turn. See [Step Rules](/docs/modules/agents#step-rules).","required":false,"type":"array","in":"body"},{"name":"boundary_policy","description":"Restricts which SOAT actions the agent may invoke. Evaluated as the intersection with the caller's own policy, so it can only narrow.","required":false,"type":"object","in":"body"},{"name":"temperature","description":"Sampling temperature passed to the model. Omitted, the provider's default applies.","required":false,"type":"number","in":"body"},{"name":"knowledge_config","description":"Knowledge search run before every generation; its matches are prepended as reference context. See [Knowledge Config](/docs/modules/agents#knowledge-config).","required":false,"type":"object","in":"body"},{"name":"output_schema","description":"JSON Schema describing the structured object the model must return. When set, non-streaming generations constrain output to this schema and the parsed value is returned as `output.object`. The schema is enforced on the way back, not just sent to the model: an object that violates it fails the generation with 502 `OUTPUT_SCHEMA_VALIDATION_FAILED`, naming the violated field. Constraints beyond `required`/`type` (`minLength`, `enum`, `pattern`, `minItems`) are honored and are what reject a structurally valid but degenerate answer. See the Structured Output section in the Agents module docs.","required":false,"type":"object","in":"body"},{"name":"prompt_caching","description":"Prompt caching for this agent's turns. `{\"enabled\": true}` marks two cache breakpoints: the end of the system block, which covers the tool definitions and the instructions, and the last message of each step, which covers the conversation so far. A provider that caches by explicit breakpoint then reads, on every later step and every later turn of the session, the whole prefix the previous one sent, and writes only what was appended. Null or omitted is off: a cache write costs more than an uncached token, so an agent whose prefix is never re-read would pay for the privilege. A prefix below the model's minimum cacheable length is not cached. Cache reads are reported as `cached_tokens` and cache writes as `cache_write_tokens` on usage; a write with no `cache_write_tokens` price bills at the input rate.","required":false,"type":"object","in":"body"},{"name":"max_context_messages","description":"Maximum number of recent messages included in the context window. Null means no limit.","required":false,"type":"integer","in":"body"},{"name":"max_output_tokens","description":"Maximum number of tokens the model may generate per step of a turn. A response that reaches it stops with `stop_reason` `length`. Null leaves the model's default.","required":false,"type":"integer","in":"body"},{"name":"reasoning","description":"How much the model reasons before answering. Translated per provider (Anthropic thinking, Gemini thinking config, OpenAI reasoning effort) against what the model supports; a model that cannot reason ignores it. `none` turns reasoning off where the model allows it. Reasoning tokens are metered as output. Null leaves the model's default.","required":false,"type":"object","in":"body"},{"name":"single_session_per_actor","description":"When true, only one open session per actor_id is allowed for this agent.","required":false,"type":"boolean","in":"body"},{"name":"trace_content_mode","description":"Zero-retention opt-in for this agent. `null` inherits the project's setting; `none` means trace and generation content is never written. Setting `full` under a project whose own mode is `none` is refused with 400 — the project is a floor an agent may only tighten.","required":false,"type":"string","in":"body"},{"name":"on_approval_expiry","description":"What happens when one of this agent's held tool calls expires un-approved. `null` (the default) and `terminate` end the chain there — the expired approval, its `approvals.expired` event and the auto-filed `approval_expired` exception are the whole record. `react` spawns a continuation that reports the staleness to the agent, for an agent that acts on it.","required":false,"type":"string","in":"body"},{"name":"version_label","description":"Optional tag for the config version this write archives (e.g. `initial`). Annotates the version only — it is not stored on the agent and is not part of the config, so labelling a change is never itself a change.","required":false,"type":"string","in":"body"}]},"get-agent":{serviceClass:"Agents",operationId:"getAgent",description:"Returns a single agent by ID. A credential scoped to a project the agent is shared with, through an accepted share, reads its `id` and `name` only.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"get",pathParams:["agent_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"}]},"update-agent":{serviceClass:"Agents",operationId:"updateAgent",description:"Updates an existing agent. Identical to PATCH — both perform partial updates.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"put",pathParams:["agent_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"ai_provider_id","description":"Public ID of the AI provider to pin. Mutually exclusive with `model_route_id`; set to `null` when switching to a route.","required":false,"type":"string","in":"body"},{"name":"model_route_id","description":"Model route in the same project. Mutually exclusive with `ai_provider_id` and `model`; set to null to clear.","required":false,"type":"string","in":"body"},{"name":"name","description":"Display name; `null` clears it.","required":false,"type":"string","in":"body"},{"name":"instructions","description":"System instructions, sent as the system message of every generation; `null` clears them.","required":false,"type":"string","in":"body"},{"name":"model","description":"Model identifier on the pinned AI provider; `null` falls back to the provider's default model. Cannot be combined with `model_route_id`.","required":false,"type":"string","in":"body"},{"name":"tool_bindings","description":"Tools attached to the agent — the only attachment field. Replaces the whole binding list; set to `null` to clear. See [Tool Bindings](/docs/modules/agents#tool-bindings).","required":false,"type":"array","in":"body"},{"name":"max_steps","description":"Maximum agent loop steps per turn; `null` restores the default of `20`.","required":false,"type":"integer","in":"body"},{"name":"tool_choice","description":"Tool choice strategy. Accepts a string (`\"auto\"`, `\"required\"`) or an object (`{ \"type\": \"tool\", \"tool_name\": \"my_tool\" }`). A forcing value (`\"required\"` or the object form) forbids a final assistant message on every step of every turn, including a resumed or continued one, so it requires a `has_tool_call` entry in `stop_conditions` — otherwise the write is refused with `FORCED_TOOL_CHOICE_CANNOT_STOP`.","required":false,"type":"unknown","in":"body"},{"name":"stop_conditions","description":"Conditions that end the agent's work early, on top of `max_steps`. Two scopes:\n\n`{\"type\": \"has_tool_call\", \"tool_name\": \"<resolved tool name>\"}` ends the **turn** after the step that calls the named tool. It narrows when the loop ends — it never lets it run past `max_steps`.\n\n`{\"type\": \"max_chain_generations\", \"max_generations\": <n>}` bounds the **continuation chain** instead: once the chain has spawned that many generations, further resumptions stop with `chain_limit` rather than extending it. It never shortens a turn. The effective ceiling is the smaller of this and the deployment's `MAX_CONTINUATION_CHAIN_GENERATIONS`, so an agent can be stricter than the platform but never looser.\n\nAn unknown `type`, a `has_tool_call` without a `tool_name`, a `max_chain_generations` whose `max_generations` is not a positive integer, or a non-object entry is rejected with 400.","required":false,"type":"array","in":"body"},{"name":"active_tool_ids","description":"Persisted tools from `tool_bindings` the model sees on every step. `null` or `[]` leaves every bound tool active. See [Active Tools](/docs/modules/agents#active-tools).","required":false,"type":"array","in":"body"},{"name":"guardrail_ids","description":"Guardrails attached at the agent scope.","required":false,"type":"array","in":"body"},{"name":"step_rules","description":"Per-step overrides of `tool_choice` and `active_tool_ids`, numbered from the first step of the turn; `null` clears them. See [Step Rules](/docs/modules/agents#step-rules).","required":false,"type":"array","in":"body"},{"name":"boundary_policy","description":"Restricts which SOAT actions the agent may invoke. Evaluated as the intersection with the caller's own policy, so it can only narrow.","required":false,"type":"object","in":"body"},{"name":"temperature","description":"Sampling temperature passed to the model; `null` restores the provider's default.","required":false,"type":"number","in":"body"},{"name":"knowledge_config","description":"Knowledge search run before every generation; its matches are prepended as reference context. `null` turns it off. See [Knowledge Config](/docs/modules/agents#knowledge-config).","required":false,"type":"object","in":"body"},{"name":"output_schema","description":"JSON Schema describing the structured object the model must return. When set, non-streaming generations constrain output to this schema and the parsed value is returned as `output.object`. The schema is enforced on the way back, not just sent to the model: an object that violates it fails the generation with 502 `OUTPUT_SCHEMA_VALIDATION_FAILED`, naming the violated field. Constraints beyond `required`/`type` (`minLength`, `enum`, `pattern`, `minItems`) are honored and are what reject a structurally valid but degenerate answer. See the Structured Output section in the Agents module docs.","required":false,"type":"object","in":"body"},{"name":"prompt_caching","description":"Prompt caching for this agent's turns. `{\"enabled\": true}` marks two cache breakpoints: the end of the system block, which covers the tool definitions and the instructions, and the last message of each step, which covers the conversation so far. A provider that caches by explicit breakpoint then reads, on every later step and every later turn of the session, the whole prefix the previous one sent, and writes only what was appended. Null or omitted is off: a cache write costs more than an uncached token, so an agent whose prefix is never re-read would pay for the privilege. A prefix below the model's minimum cacheable length is not cached. Cache reads are reported as `cached_tokens` and cache writes as `cache_write_tokens` on usage; a write with no `cache_write_tokens` price bills at the input rate.","required":false,"type":"object","in":"body"},{"name":"max_context_messages","description":"Maximum number of recent messages included in the context window. Null means no limit.","required":false,"type":"integer","in":"body"},{"name":"max_output_tokens","description":"Maximum number of tokens the model may generate per step of a turn. A response that reaches it stops with `stop_reason` `length`. Null leaves the model's default.","required":false,"type":"integer","in":"body"},{"name":"reasoning","description":"How much the model reasons before answering. Translated per provider (Anthropic thinking, Gemini thinking config, OpenAI reasoning effort) against what the model supports; a model that cannot reason ignores it. `none` turns reasoning off where the model allows it. Reasoning tokens are metered as output. Null leaves the model's default.","required":false,"type":"object","in":"body"},{"name":"single_session_per_actor","description":"When true, only one open session per actor_id is allowed for this agent.","required":false,"type":"boolean","in":"body"},{"name":"trace_content_mode","description":"Zero-retention opt-in for this agent. `null` inherits the project's setting; `none` means trace and generation content is never written. Setting `full` under a project whose own mode is `none` is refused with 400.","required":false,"type":"string","in":"body"},{"name":"on_approval_expiry","description":"What happens when one of this agent's held tool calls expires un-approved. `null` (the default) and `terminate` end the chain there — the expired approval, its `approvals.expired` event and the auto-filed `approval_expired` exception are the whole record. `react` spawns a continuation that reports the staleness to the agent, for an agent that acts on it.","required":false,"type":"string","in":"body"},{"name":"version_label","description":"Optional tag for the config version this write archives (e.g. `pre-tone-change`). Annotates the version only — it is not stored on the agent and is not part of the config, so labelling a change is never itself a change. Ignored when the write changes nothing, since no version is created.","required":false,"type":"string","in":"body"},{"name":"expected_version","description":"Refuses the write unless the resource is at this version.","required":false,"type":"integer","in":"body"}]},"patch-agent":{serviceClass:"Agents",operationId:"patchAgent",description:"Partially updates an existing agent. Identical to PUT — both perform partial updates.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"patch",pathParams:["agent_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"ai_provider_id","description":"Public ID of the AI provider to pin. Mutually exclusive with `model_route_id`; set to `null` when switching to a route.","required":false,"type":"string","in":"body"},{"name":"model_route_id","description":"Model route in the same project. Mutually exclusive with `ai_provider_id` and `model`; set to null to clear.","required":false,"type":"string","in":"body"},{"name":"name","description":"Display name; `null` clears it.","required":false,"type":"string","in":"body"},{"name":"instructions","description":"System instructions, sent as the system message of every generation; `null` clears them.","required":false,"type":"string","in":"body"},{"name":"model","description":"Model identifier on the pinned AI provider; `null` falls back to the provider's default model. Cannot be combined with `model_route_id`.","required":false,"type":"string","in":"body"},{"name":"tool_bindings","description":"Tools attached to the agent — the only attachment field. Replaces the whole binding list; set to `null` to clear. See [Tool Bindings](/docs/modules/agents#tool-bindings).","required":false,"type":"array","in":"body"},{"name":"max_steps","description":"Maximum agent loop steps per turn; `null` restores the default of `20`.","required":false,"type":"integer","in":"body"},{"name":"tool_choice","description":"Tool choice strategy. Accepts a string (`\"auto\"`, `\"required\"`) or an object (`{ \"type\": \"tool\", \"tool_name\": \"my_tool\" }`). A forcing value (`\"required\"` or the object form) forbids a final assistant message on every step of every turn, including a resumed or continued one, so it requires a `has_tool_call` entry in `stop_conditions` — otherwise the write is refused with `FORCED_TOOL_CHOICE_CANNOT_STOP`.","required":false,"type":"unknown","in":"body"},{"name":"stop_conditions","description":"Conditions that end the agent's work early, on top of `max_steps`. Two scopes:\n\n`{\"type\": \"has_tool_call\", \"tool_name\": \"<resolved tool name>\"}` ends the **turn** after the step that calls the named tool. It narrows when the loop ends — it never lets it run past `max_steps`.\n\n`{\"type\": \"max_chain_generations\", \"max_generations\": <n>}` bounds the **continuation chain** instead: once the chain has spawned that many generations, further resumptions stop with `chain_limit` rather than extending it. It never shortens a turn. The effective ceiling is the smaller of this and the deployment's `MAX_CONTINUATION_CHAIN_GENERATIONS`, so an agent can be stricter than the platform but never looser.\n\nAn unknown `type`, a `has_tool_call` without a `tool_name`, a `max_chain_generations` whose `max_generations` is not a positive integer, or a non-object entry is rejected with 400.","required":false,"type":"array","in":"body"},{"name":"active_tool_ids","description":"Persisted tools from `tool_bindings` the model sees on every step. `null` or `[]` leaves every bound tool active. See [Active Tools](/docs/modules/agents#active-tools).","required":false,"type":"array","in":"body"},{"name":"guardrail_ids","description":"Guardrails attached at the agent scope.","required":false,"type":"array","in":"body"},{"name":"step_rules","description":"Per-step overrides of `tool_choice` and `active_tool_ids`, numbered from the first step of the turn; `null` clears them. See [Step Rules](/docs/modules/agents#step-rules).","required":false,"type":"array","in":"body"},{"name":"boundary_policy","description":"Restricts which SOAT actions the agent may invoke. Evaluated as the intersection with the caller's own policy, so it can only narrow.","required":false,"type":"object","in":"body"},{"name":"temperature","description":"Sampling temperature passed to the model; `null` restores the provider's default.","required":false,"type":"number","in":"body"},{"name":"knowledge_config","description":"Knowledge search run before every generation; its matches are prepended as reference context. `null` turns it off. See [Knowledge Config](/docs/modules/agents#knowledge-config).","required":false,"type":"object","in":"body"},{"name":"output_schema","description":"JSON Schema describing the structured object the model must return. When set, non-streaming generations constrain output to this schema and the parsed value is returned as `output.object`. The schema is enforced on the way back, not just sent to the model: an object that violates it fails the generation with 502 `OUTPUT_SCHEMA_VALIDATION_FAILED`, naming the violated field. Constraints beyond `required`/`type` (`minLength`, `enum`, `pattern`, `minItems`) are honored and are what reject a structurally valid but degenerate answer. See the Structured Output section in the Agents module docs.","required":false,"type":"object","in":"body"},{"name":"prompt_caching","description":"Prompt caching for this agent's turns. `{\"enabled\": true}` marks two cache breakpoints: the end of the system block, which covers the tool definitions and the instructions, and the last message of each step, which covers the conversation so far. A provider that caches by explicit breakpoint then reads, on every later step and every later turn of the session, the whole prefix the previous one sent, and writes only what was appended. Null or omitted is off: a cache write costs more than an uncached token, so an agent whose prefix is never re-read would pay for the privilege. A prefix below the model's minimum cacheable length is not cached. Cache reads are reported as `cached_tokens` and cache writes as `cache_write_tokens` on usage; a write with no `cache_write_tokens` price bills at the input rate.","required":false,"type":"object","in":"body"},{"name":"max_context_messages","description":"Maximum number of recent messages included in the context window. Null means no limit.","required":false,"type":"integer","in":"body"},{"name":"max_output_tokens","description":"Maximum number of tokens the model may generate per step of a turn. A response that reaches it stops with `stop_reason` `length`. Null leaves the model's default.","required":false,"type":"integer","in":"body"},{"name":"reasoning","description":"How much the model reasons before answering. Translated per provider (Anthropic thinking, Gemini thinking config, OpenAI reasoning effort) against what the model supports; a model that cannot reason ignores it. `none` turns reasoning off where the model allows it. Reasoning tokens are metered as output. Null leaves the model's default.","required":false,"type":"object","in":"body"},{"name":"single_session_per_actor","description":"When true, only one open session per actor_id is allowed for this agent.","required":false,"type":"boolean","in":"body"},{"name":"trace_content_mode","description":"Zero-retention opt-in for this agent. `null` inherits the project's setting; `none` means trace and generation content is never written. Setting `full` under a project whose own mode is `none` is refused with 400.","required":false,"type":"string","in":"body"},{"name":"on_approval_expiry","description":"What happens when one of this agent's held tool calls expires un-approved. `null` (the default) and `terminate` end the chain there — the expired approval, its `approvals.expired` event and the auto-filed `approval_expired` exception are the whole record. `react` spawns a continuation that reports the staleness to the agent, for an agent that acts on it.","required":false,"type":"string","in":"body"},{"name":"version_label","description":"Optional tag for the config version this write archives (e.g. `pre-tone-change`). Annotates the version only — it is not stored on the agent and is not part of the config, so labelling a change is never itself a change. Ignored when the write changes nothing, since no version is created.","required":false,"type":"string","in":"body"},{"name":"expected_version","description":"Refuses the write unless the resource is at this version.","required":false,"type":"integer","in":"body"}]},"delete-agent":{serviceClass:"Agents",operationId:"deleteAgent",description:"Deletes an agent by ID. Fails with `409` if the agent has dependent generations or traces, or another project has accepted a share of it, unless `force=true` is passed, in which case those generations and traces are deleted along with the agent and the shares are revoked. Every share of the agent is revoked when it is deleted.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"delete",pathParams:["agent_id"],queryParams:["force"],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"force","description":"When `true`, deletes the agent's dependent generations and traces, revokes its accepted shares and leaves its ingestion rules naming it instead of returning `409 AGENT_HAS_DEPENDENTS`.\n","required":false,"type":"boolean","in":"query"}]},"create-agent-generation":{serviceClass:"Agents",operationId:"createAgentGeneration",description:"Sends messages to the agent, resolves its tools, and runs the AI model loop. Background by default: returns `202 Accepted` with a `generation_id` to poll via `GET /api/v1/generations/{generation_id}`. Pass `?wait=true` to block and receive the result inline, where client tools pause the generation and return `requires_action`. Streaming (`stream: true`) implies waiting. Pass `idempotency_key` to make a retry safe: a request whose key is already claimed runs nothing and answers `202` with the generation the key names. A credential scoped to a project the agent is shared with, through an accepted share, runs it in that project: on the publisher's configuration, recorded, metered and governed in the grantee project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"post",pathParams:["agent_id"],queryParams:["wait"],headerParams:["Idempotency-Key"],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"Idempotency-Key","description":"The request's deduplication key. Equivalent to `idempotency_key` in the request body; sending both with different values is `400 VALIDATION_FAILED`. A non-empty string of at most 255 characters.","required":false,"type":"string","in":"header"},{"name":"wait","description":"When omitted or `false` (default), the generation runs in the background and `202 Accepted` is returned immediately with a `generation_id` to poll. Pass `true` to block until the generation settles and receive the result. Mutually exclusive with `stream: true`. A tool call, `builtin` or MCP, always waits.","required":false,"type":"boolean","in":"query"},{"name":"messages","description":"Conversation turns, oldest first. Roles are `user` and `assistant`: the system prompt is the agent's `instructions`, so a `system` entry is refused with `400 SYSTEM_MESSAGE_NOT_ALLOWED`.","required":true,"type":"array","in":"body"},{"name":"stream","description":"When true the response is an SSE stream","required":false,"type":"boolean","in":"body"},{"name":"trace_id","description":"Optional trace ID to group generations. Each generation appends its own steps to the trace's steps object, and `step_count` covers them all.","required":false,"type":"string","in":"body"},{"name":"parent_trace_id","description":"The trace ID of the parent agent generation that triggered this one (for agent-to-agent calls)","required":false,"type":"string","in":"body"},{"name":"root_trace_id","description":"The trace ID of the root generation in the call chain; if omitted, this generation is the root","required":false,"type":"string","in":"body"},{"name":"max_call_depth","description":"Maximum nested agent-call depth; 0 short-circuits with a depth-guard response","required":false,"type":"integer","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as `X-Soat-Context-<key>` headers on every `http`, `mcp` and `builtin` tool call in this generation. The header name is the deployment's configured context prefix (`X-Soat-Context-` by default) plus the key verbatim — no character is re-cased. Keys are never case-converted — they round-trip exactly as sent. An invalid or colliding key is rejected with `400 INVALID_TOOL_CONTEXT_KEY`.","required":false,"type":"object","in":"body"},{"name":"action_id","description":"Logical action label recorded on the generation's usage meter, so spend can be rolled up per action (e.g. an A/B/C/D operating action).","required":false,"type":"string","in":"body"},{"name":"guardrail_context","description":"Caller-supplied guardrail context (the `context.*` namespace guard and class expressions read at tool-dispatch time). Free-form and never interpreted by the platform; a guardrail may combine it with a `context_tool` per its `context_mode`. See the guardrails module.","required":false,"type":"object","in":"body"},{"name":"metadata","description":"Caller-supplied key/value metadata attached to the generation record for per-run audit attribution (e.g. the ticket or case this action belongs to). Round-trips verbatim when the generation is fetched via the generations API. The bag is caller-owned and no key is reserved: server-owned state (usage attribution, the served agent version, the model route's record, the extraction summary, what knowledge retrieval served) lives in its own top-level generation fields and cannot be written from here. Use the request's own `action_id` field to set the usage-attribution label.","required":false,"type":"object","in":"body"},{"name":"idempotency_key","description":"Deduplication key, unique within the project, that makes a retry of an at-least-once delivery or an ambiguous failure safe. The first request under a key runs the generation; any later request carrying it runs nothing and answers `202` with the generation the key names, whatever state it has reached — including a retry that arrives while the original is still running. The key is claimed by the generation record for as long as the record exists.\n\nEvery other body field except `stream` is the request the key names: reusing a key with any of them changed, or on another agent of the project, is `409 IDEMPOTENCY_KEY_REUSED`. `stream` and `wait` say how the caller receives the generation, not what it is, so a retry may flip them.","required":false,"type":"string","in":"body"},{"name":"knowledge_config","description":"Per-generation knowledge retrieval override. Array filters (memory_store_ids, document_ids, document_paths) are unioned with the agent's stored knowledge_config; `tags` pairs are merged with the override winning per key; scalar fields (min_score, rrf_k, recency_half_life_days, limit) use the per-generation value when present.","required":false,"type":"object","in":"body"}]},"submit-agent-tool-outputs":{serviceClass:"Agents",operationId:"submitAgentToolOutputs",description:"Resumes a generation that was paused due to client tool calls. Provide tool outputs for each pending tool call.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"post",pathParams:["agent_id","generation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"generation_id","description":"Public ID of the generation paused at `requires_action`","required":true,"type":"string","in":"path"},{"name":"tool_outputs","description":"Results for the tool calls the paused generation is waiting on, one entry per `tool_call_id`.","required":true,"type":"array","in":"body"}]},"list-agent-versions":{serviceClass:"AgentVersions",operationId:"listAgentVersions",description:"Returns the agent's archived configurations, newest first. A version is written on create and on every subsequent write that changes the config — through the REST API or a formation apply alike. See [Versioning and Staged Rollout](/docs/modules/agents#versioning-and-staged-rollout).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"get",pathParams:["agent_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-agent-version":{serviceClass:"AgentVersions",operationId:"getAgentVersion",description:"Returns the exact configuration the agent held at a given version, so a generation can be traced back to the config that produced it.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"get",pathParams:["agent_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"version","description":"Archived config version number","required":true,"type":"integer","in":"path"}]},"restore-agent-version":{serviceClass:"AgentVersions",operationId:"restoreAgentVersion",description:"Copies the named version's configuration onto the agent as a **new** version rather than rewinding the counter, so history stays append-only and the versions in between remain retrievable. Restoring the config the agent already holds is a no-op and creates no version. The restored config fully replaces the current one: a field the archived version did not set is cleared, not merged. Restore re-validates the config, so a tool, provider, or guardrail deleted since the snapshot was taken fails the request instead of writing a broken agent.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"post",pathParams:["agent_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"version","description":"Archived config version number","required":true,"type":"integer","in":"path"},{"name":"label","description":"Tag for the version this restore creates. Defaults to `restored from v{version}`.","required":false,"type":"string","in":"body"}]},"set-agent-release":{serviceClass:"AgentVersions",operationId:"setAgentRelease",description:"Starts serving two archived versions side by side: `canary_percent` of traffic gets `canary_version`, the rest gets `stable_version`. Assignment is deterministic — it hashes the actor behind the request's session (falling back to the session itself), so one end user never flip-flops between configs mid-conversation. Requests with neither are split randomly. While a release is active the agent's live columns act as a **draft**: further edits archive new versions but do not disturb either side of the running split. End the rollout with `promote` or `abort`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"put",pathParams:["agent_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"},{"name":"stable_version","description":"An existing version to serve as the baseline","required":true,"type":"integer","in":"body"},{"name":"canary_version","description":"An existing version to trial. Must differ from `stable_version`.","required":true,"type":"integer","in":"body"},{"name":"canary_percent","description":"Percentage of traffic to assign to `canary_version`","required":true,"type":"integer","in":"body"},{"name":"promotion_gate","description":"Eval to gate promotion on. It must belong to this project and evaluate this agent; anything else is a `400`. Omit it, or send null, for a rollout that can be promoted at will.","required":false,"type":"string","in":"body"}]},"promote-agent-release":{serviceClass:"AgentVersions",operationId:"promoteAgentRelease",description:"Makes the canary version's config the agent's live config and clears the release. The canary is pinned by version, so an edit that landed mid-rollout is not promoted in its place — it stays an unreleased draft in the version history. When the release carries a `promotion_gate`, the eval it names must have a run that finished `completed` with `passed: true` **and** was pinned to the canary version (`agent_version`); otherwise the call is a `409` and the rollout is left running untouched. The run that cleared the gate is recorded as `eval_run_id` on the version that goes live.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"post",pathParams:["agent_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"}]},"abort-agent-release":{serviceClass:"AgentVersions",operationId:"abortAgentRelease",description:"Restores the stable version's config as the agent's live config and clears the release, so all traffic returns to the configuration the rollout was measured against — not to whatever draft the live columns happened to hold.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/agents",httpMethod:"post",pathParams:["agent_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent public ID","required":true,"type":"string","in":"path"}]},"list-ai-providers":{serviceClass:"AIProviders",operationId:"listAiProviders",description:"Returns a list of AI provider configurations for a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ai-providers",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-ai-provider":{serviceClass:"AIProviders",operationId:"createAiProvider",description:"Creates a new LLM provider configuration. A `bedrock` or `vertex` record must carry a credential of its own — a linked `secret_id`, or an `apiKey` in `config`. Without one the provider SDK signs with the server's own credentials (the AWS default credential chain, Google Application Default Credentials), which is refused with `400 VALIDATION_FAILED` unless the deployment allows it.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ai-providers",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"name","description":"Provider configuration name","required":true,"type":"string","in":"body"},{"name":"provider","description":"LLM provider","required":true,"type":"string","in":"body"},{"name":"default_model","description":"Default model to use","required":true,"type":"string","in":"body"},{"name":"secret_id","description":"Secret ID containing API credentials. Required for `bedrock` and `vertex` unless `config.apiKey` carries one.","required":false,"type":"string","in":"body"},{"name":"base_url","description":"Custom base URL for the provider","required":false,"type":"string","in":"body"},{"name":"config","description":"Additional provider-specific configuration","required":false,"type":"object","in":"body"}]},"get-ai-provider":{serviceClass:"AIProviders",operationId:"getAiProvider",description:"Returns a specific AI provider configuration",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ai-providers",httpMethod:"get",pathParams:["ai_provider_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ai_provider_id","description":"AI Provider ID","required":true,"type":"string","in":"path"}]},"update-ai-provider":{serviceClass:"AIProviders",operationId:"updateAiProvider",description:"Updates an AI provider configuration",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ai-providers",httpMethod:"patch",pathParams:["ai_provider_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ai_provider_id","description":"AI Provider ID","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"provider","description":"LLM provider","required":false,"type":"string","in":"body"},{"name":"default_model","description":"","required":false,"type":"string","in":"body"},{"name":"secret_id","description":"","required":false,"type":"string","in":"body"},{"name":"base_url","description":"","required":false,"type":"string","in":"body"},{"name":"config","description":"","required":false,"type":"object","in":"body"}]},"delete-ai-provider":{serviceClass:"AIProviders",operationId:"deleteAiProvider",description:"Deletes an AI provider configuration. Live references — chats, agents, and model routes whose targets name this provider — always block deletion with `409 AI_PROVIDER_HAS_DEPENDENTS`; `force` does not override them, so delete or repoint those resources first. Soft dependents — price overrides and usage/generation records — also block with `409` unless `force=true`, which deletes the provider's price overrides and unlinks its usage history, preserving those rows: each usage event keeps the provider's id as its `ai_provider_id`. The `409` body's `error.meta` reports the counts, a sample of offending IDs, and a `forcible` flag that is `true` when a `force=true` retry would succeed.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ai-providers",httpMethod:"delete",pathParams:["ai_provider_id"],queryParams:["force"],headerParams:[],cookieParams:[],flags:[{"name":"ai_provider_id","description":"AI Provider ID","required":true,"type":"string","in":"path"},{"name":"force","description":"When `true`, delete the provider's price overrides and unlink its usage history so a provider with only soft dependents can be removed. Has no effect on live references (chats, agents, model routes), which always block deletion.\n","required":false,"type":"boolean","in":"query"}]},"list-ai-provider-models":{serviceClass:"AIProviders",operationId:"listAiProviderModels",description:"Asks the provider which models it can run, using this provider record's own credentials and configuration, and returns provider-native model ids — the same strings `default_model` and an agent's `model` carry. Which models are reachable is a property of the credential, not of the provider type: a Vertex provider sees only the publisher models its Google Cloud project and location serve, and a Bedrock provider only the foundation models enabled in its region. Reading the list is how a caller avoids pinning a model that fails at generation time. Not every provider type can answer. `azure` lists deployments an operator named rather than models, and `ollama` lists whatever was pulled onto that host, so both return `400 MODEL_LISTING_UNSUPPORTED`. Listing resolves credentials the same way generation does, so a record that can generate can list. The API-key types (`openai`, `groq`, `xai`, `gateway`, `custom`, `anthropic`, `google`) use the record's linked secret and cannot list without one. `bedrock` and `vertex` use the linked secret — IAM keys or a Bedrock API key, a Google service-account key. A record with no `secret_id` would fall back to the server's own credentials (the AWS default credential chain, Google Application Default Credentials); it can list only on a deployment that allows a record to use them, and returns `400 AI_PROVIDER_MISCONFIGURED` otherwise. A Vertex record needs no `config.project` when its secret is a service-account key, since the key file names its own project. A Vertex record in express mode (API key) cannot list at all: the publisher-model listing rejects API keys and needs a credential that asserts a principal, so it returns `400 MODEL_LISTING_UNSUPPORTED`. The Vertex answer is the publisher catalogue the record's `config.location` region serves. The project behind the credential is billed and quota'd for the call but does not filter the result, so a listed model may still be unavailable to that project at generation time. The provider answers its whole catalogue in one call; `q`, `limit` and `offset` narrow and page what it returned. A request without `limit` gets the first 50 models.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ai-providers",httpMethod:"get",pathParams:["ai_provider_id"],queryParams:["q","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"ai_provider_id","description":"AI Provider ID","required":true,"type":"string","in":"path"},{"name":"q","description":"Keeps the models whose `id` or `display_name` contains this text, ignoring case. Applied before paging, so `total` counts the matches.\n","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-ai-provider-prices":{serviceClass:"AIProviders",operationId:"getAiProviderPrices",description:"Returns the per-provider price overrides for this AI provider instance. An override prices this specific provider (e.g. an enterprise-negotiated rate or a gateway with markup) and wins over the global default at cost time. Authorized by the caller's access to the provider's project — so, unlike the global price book, a project's own overrides are visible here.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ai-providers",httpMethod:"get",pathParams:["ai_provider_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ai_provider_id","description":"AI Provider ID","required":true,"type":"string","in":"path"}]},"update-ai-provider-prices":{serviceClass:"AIProviders",operationId:"updateAiProviderPrices",description:"Upserts price overrides for this AI provider instance, keyed on (model, effective_from). The provider slug is taken from the AI provider itself, so only the model, rates, and effective_from are supplied. Authorized by the caller's access to the provider's project. `effective_from` must be in the future once the (model, component) has a price row — past prices are immutable, so corrections ship as new future-dated rows. A first price for a (model, component) nothing prices yet may be dated now or earlier, so a new provider is never live and unpriced.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ai-providers",httpMethod:"put",pathParams:["ai_provider_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ai_provider_id","description":"AI Provider ID","required":true,"type":"string","in":"path"},{"name":"prices","description":"","required":true,"type":"array","in":"body"}]},"list-api-keys":{serviceClass:"APIKeys",operationId:"listApiKeys",description:"Lists API keys accessible to the caller. - JWT admin: returns all API keys. - JWT regular user: returns only the user's own API keys. - Project-scoped credential (API key or OAuth token): returns the caller's own keys scoped to that project, or every key scoped to it when the credential holds `api-keys:ListApiKeys` on the project. Confinement to a project is not authority over it, and a key's metadata names its owner, prefix and attached policies.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/api-keys",httpMethod:"get",pathParams:[],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-api-key":{serviceClass:"APIKeys",operationId:"createApiKey",description:"Creates a new API key for the authenticated user. - `project_id` is optional. When set, the key is scoped to that single project. When omitted or null, the key is **unscoped** and spans every project its owner can reach. - If `policy_ids` is provided, the key's effective permissions are the intersection of the user's policies and the key's policies. - Otherwise the key inherits the user's permissions (confined to the key's project when scoped). - When the request is authenticated with a **project-scoped credential**, the new key is confined to that same project: omitting `project_id` defaults to it, naming a different project returns `403 API_KEY_PROJECT_SCOPE`, and `project_id: null` (an unscoped key) is likewise refused. Minting an unscoped key requires an unscoped credential.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/api-keys",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"name","description":"Key name for identification","required":true,"type":"string","in":"body"},{"name":"project_id","description":"Optional project ID to scope the key to. Omit or set null to create an unscoped key that spans projects.","required":false,"type":"string","in":"body"},{"name":"policy_ids","description":"Optional list of policy IDs to attach. Key permissions become the intersection of user policies and these policies.","required":false,"type":"array","in":"body"}]},"get-api-key":{serviceClass:"APIKeys",operationId:"getApiKey",description:"Returns details of an API key. Only the owner or an admin can access it, and a project-scoped credential can only reach keys in its own project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/api-keys",httpMethod:"get",pathParams:["api_key_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"api_key_id","description":"API key public ID (key_ prefix)","required":true,"type":"string","in":"path"}]},"update-api-key":{serviceClass:"APIKeys",operationId:"updateApiKey",description:"Updates an API key's name, project scope, or policies. The project scope can be changed to another project, set (scoping a previously unscoped key), or cleared with null (unscoping the key). Only the owner or an admin can update it. A project-scoped credential can only update keys in its own project, and cannot move a key to another project or unscope it.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/api-keys",httpMethod:"put",pathParams:["api_key_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"api_key_id","description":"API key public ID (key_ prefix)","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"project_id","description":"Re-scope the key to a different project, or set null to clear the scope (unscoped key). Omit to leave the scope unchanged.","required":false,"type":"string","in":"body"},{"name":"policy_ids","description":"Replace the key's policy list (empty array removes all)","required":false,"type":"array","in":"body"}]},"delete-api-key":{serviceClass:"APIKeys",operationId:"deleteApiKey",description:"Deletes an API key. Only the owner or an admin can delete it, and a project-scoped credential can only delete keys in its own project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/api-keys",httpMethod:"delete",pathParams:["api_key_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"api_key_id","description":"API key public ID (key_ prefix)","required":true,"type":"string","in":"path"}]},"list-approvals":{serviceClass:"Approvals",operationId:"listApprovals",description:"Returns approval items for a project, filterable by status, origin, and expiry.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/approvals",httpMethod:"get",pathParams:[],queryParams:["project_id","status","origin","expires_before","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"status","description":"Filter by lifecycle status","required":false,"type":"string","in":"query"},{"name":"origin","description":"Filter by producer origin","required":false,"type":"string","in":"query"},{"name":"expires_before","description":"Return only items expiring at or before this timestamp","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"list-approval-recurrences":{serviceClass:"Approvals",operationId:"listApprovalRecurrences",description:"Read-only rollup answering \"what keeps coming back?\" — groups items by `dedup_key` and returns those recurring at least `min_count` times, most-recurrent first. Each group carries the ordered item chain (via `previous_item_id`) and the resolution reasons in order, so a human can read recurring rejections side by side and graduate the pattern into a guardrail `deny`. Exact-key grouping only; no cluster state is stored.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/approvals",httpMethod:"get",pathParams:[],queryParams:["project_id","status","min_count","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"status","description":"Lifecycle status the groups are built from (default `rejected`)","required":false,"type":"string","in":"query"},{"name":"min_count","description":"Minimum items in a group for it to be returned","required":false,"type":"integer","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-approval":{serviceClass:"Approvals",operationId:"getApproval",description:"Returns a single approval item with its full evidence.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/approvals",httpMethod:"get",pathParams:["approval_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"approval_id","description":"Approval item ID","required":true,"type":"string","in":"path"}]},"approve-approval":{serviceClass:"Approvals",operationId:"approveApproval",description:"Approves the item. Optionally supply edited `arguments` to replace the proposed arguments (edit-then-approve); the original is preserved on the item. Expiry is re-checked at decision time — an expired item can never be approved.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/approvals",httpMethod:"post",pathParams:["approval_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"approval_id","description":"Approval item ID","required":true,"type":"string","in":"path"},{"name":"arguments","description":"Edited arguments to execute instead of the proposed ones. Editing composes a new call, so it additionally requires permission to make that call: `tools:CallTool` on the tool, and for a `builtin` proposal the action's own IAM action.","required":false,"type":"object","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as context headers on the approved action and on every tool call of the continuation turn, as on a generation's `tool_context`. Never stored on the item: supply it on the approve call itself, for a tool that authorizes with a credential minted at decision time. The server-pinned identity keys (`session_id`, `actor_id`, `actor_external_id`) are dropped. An invalid or colliding key is rejected with `400 INVALID_TOOL_CONTEXT_KEY` and nothing is resolved.","required":false,"type":"object","in":"body"}]},"reject-approval":{serviceClass:"Approvals",operationId:"rejectApproval",description:"Rejects the item. A reason is required and preserved on the item.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/approvals",httpMethod:"post",pathParams:["approval_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"approval_id","description":"Approval item ID","required":true,"type":"string","in":"path"},{"name":"reason","description":"Why the item is being rejected (required)","required":true,"type":"string","in":"body"}]},"list-audit-entries":{serviceClass:"AuditLog",operationId:"listAuditEntries",description:"Returns audit-log entries visible to the caller, newest first. All filters are optional and combine with AND. `resource_srn` is a prefix match (e.g. `srn:{project}:secret:` matches every secret action); every other filter is exact.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/audit-log",httpMethod:"get",pathParams:[],queryParams:["project_id","action","principal_id","resource_public_id","resource_srn","from","to","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (scopes results; required if not using project key auth for a specific project)","required":false,"type":"string","in":"query"},{"name":"action","description":"Exact permission-action string, e.g. `secrets:DeleteSecret`","required":false,"type":"string","in":"query"},{"name":"principal_id","description":"Public id of the principal (`user_…` or `key_…`)","required":false,"type":"string","in":"query"},{"name":"resource_public_id","description":"Exact target resource public id, e.g. `sec_…`","required":false,"type":"string","in":"query"},{"name":"resource_srn","description":"SRN prefix match, e.g. `srn:{project}:secret:`. The log is append-only, so a stored SRN is never rewritten; the filter matches it as stored.","required":false,"type":"string","in":"query"},{"name":"from","description":"Only entries created at or after this timestamp (ISO 8601)","required":false,"type":"string","in":"query"},{"name":"to","description":"Only entries created at or before this timestamp (ISO 8601)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above 200 it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"export-audit-entries":{serviceClass:"AuditLog",operationId:"exportAuditEntries",description:"Streams a project's audit-log entries as newline-delimited JSON — one entry object per line, oldest first — for archival before the retention window expires, or for shipping into an external system. `project_id` is required: the export is per-project by design. Filters behave exactly as they do on the list endpoint.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/audit-log",httpMethod:"get",pathParams:[],queryParams:["project_id","action","principal_id","resource_public_id","resource_srn","from","to"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project whose entries are exported","required":true,"type":"string","in":"query"},{"name":"action","description":"Exact permission-action string, e.g. `secrets:DeleteSecret`","required":false,"type":"string","in":"query"},{"name":"principal_id","description":"Public id of the principal (`user_…` or `key_…`)","required":false,"type":"string","in":"query"},{"name":"resource_public_id","description":"Exact target resource public id, e.g. `sec_…`","required":false,"type":"string","in":"query"},{"name":"resource_srn","description":"SRN prefix match, e.g. `srn:{project}:secret:`. The log is append-only, so a stored SRN is never rewritten; the filter matches it as stored.","required":false,"type":"string","in":"query"},{"name":"from","description":"Only entries created at or after this timestamp (ISO 8601)","required":false,"type":"string","in":"query"},{"name":"to","description":"Only entries created at or before this timestamp (ISO 8601)","required":false,"type":"string","in":"query"}]},"get-audit-entry":{serviceClass:"AuditLog",operationId:"getAuditEntry",description:"Returns a single audit-log entry, including its `detail` payload",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/audit-log",httpMethod:"get",pathParams:["entry_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"entry_id","description":"Audit entry ID","required":true,"type":"string","in":"path"}]},"list-chains":{serviceClass:"Chains",operationId:"listChains",description:"Returns the continuation chains in a project, newest first. Filter by `status` to find the chains that may still be spending (`active`) or the ones a budget stopped (`budget_exhausted`).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/chains",httpMethod:"get",pathParams:[],queryParams:["project_id","status","agent_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"status","description":"Filter by chain status","required":false,"type":"string","in":"query"},{"name":"agent_id","description":"Filter by the agent whose continuation opened the chain","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-chain":{serviceClass:"Chains",operationId:"getChain",description:"Returns a single continuation chain. To read the generations in it, list generations filtered by `chain_id`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/chains",httpMethod:"get",pathParams:["chain_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"chain_id","description":"Continuation chain ID","required":true,"type":"string","in":"path"}]},"list-chats":{serviceClass:"Chats",operationId:"listChats",description:"Returns all chats in the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/chats",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID to filter by","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-chat":{serviceClass:"Chats",operationId:"createChat",description:"Creates a new chat resource bound to an AI provider.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/chats",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ai_provider_id","description":"Public ID of the AI provider to pin. Optional: omit it to inherit the project's `default_model_route_id`, which gives the chat provider failover. Omitting it returns `400` when the project has no default, and cannot be combined with `model` — each route target names its own.","required":false,"type":"string","in":"body"},{"name":"project_id","description":"Public ID of the project. Required when the user belongs to multiple projects and no project key is used.\n","required":false,"type":"string","in":"body"},{"name":"name","description":"Optional human-readable name","required":false,"type":"string","in":"body"},{"name":"instructions","description":"Optional system message applied to all completions on this chat","required":false,"type":"string","in":"body"},{"name":"model","description":"Optional default model override","required":false,"type":"string","in":"body"}]},"get-chat":{serviceClass:"Chats",operationId:"getChat",description:"Returns a single chat by ID.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/chats",httpMethod:"get",pathParams:["chat_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"chat_id","description":"","required":true,"type":"string","in":"path"}]},"delete-chat":{serviceClass:"Chats",operationId:"deleteChat",description:"Deletes a chat by ID.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/chats",httpMethod:"delete",pathParams:["chat_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"chat_id","description":"","required":true,"type":"string","in":"path"}]},"create-chat-completion":{serviceClass:"Chats",operationId:"createChatCompletion",description:"OpenAI Chat Completions-compatible endpoint. Mirrors OpenAI's `POST /v1/chat/completions` path so an OpenAI SDK can target it by base URL alone. Names exactly one target. With `ai_provider_id` the completion is stateless: the provider's secret is decrypted and the appropriate Vercel AI SDK provider is called, with no server-side model fallback. With `chat_id` the stored chat supplies the provider (or the project's `default_model_route_id`), model and instructions. System content travels only in `instructions` — a `role: \"system\"` entry in `messages` is refused with `400 SYSTEM_MESSAGE_NOT_ALLOWED`. With `chat_id`, a request `instructions` replaces the chat's stored one for this call only; the stored value applies when the request carries none, and the two are never merged. Messages may use `document_id` instead of `content` with either target. Chats hold no message history — send the full `messages` array every time.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/chats",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ai_provider_id","description":"Public ID of the AI provider to run the completion against. Mutually exclusive with `chat_id`; exactly one of the two is required.\n","required":false,"type":"string","in":"body"},{"name":"chat_id","description":"Public ID of a stored chat supplying the provider, model and instructions. Mutually exclusive with `ai_provider_id`; exactly one of the two is required.\n","required":false,"type":"string","in":"body"},{"name":"model","description":"Model identifier. Overrides the provider's `default_model`, or the chat's `model`, when specified.\n","required":false,"type":"string","in":"body"},{"name":"instructions","description":"System instructions for this call. Sent to the provider as its `instructions` argument rather than as a message, which is the only place the AI SDK accepts system content (`allowSystemInMessages` defaults to false). This field is the only channel — a `role: \"system\"` entry in `messages` is refused with `400 SYSTEM_MESSAGE_NOT_ALLOWED`. With `chat_id`, this replaces the chat's stored `instructions` for this call only; the stored value applies when the request carries none, and the two are never merged.\n","required":false,"type":"string","in":"body"},{"name":"messages","description":"Ordered list of chat messages","required":true,"type":"array","in":"body"},{"name":"stream","description":"When `true` the response is an SSE stream of delta chunks. When `false` (default) a single JSON object is returned.\n","required":false,"type":"boolean","in":"body"}]},"list-conversations":{serviceClass:"Conversations",operationId:"listConversations",description:"Returns all conversations the caller has access to. If projectId is provided, returns only conversations in that project. project keys are scoped to a single project automatically.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"get",pathParams:[],queryParams:["project_id","actor_id","tags","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (optional)","required":false,"type":"string","in":"query"},{"name":"actor_id","description":"Filter by actor ID","required":false,"type":"string","in":"query"},{"name":"tags","description":"Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n","required":false,"type":"array","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-conversation":{serviceClass:"Conversations",operationId:"createConversation",description:"Creates a new conversation. project keys automatically infer the project from the key's scope; JWT callers must supply projectId.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID. Required for JWT auth; omit when using an project key.","required":false,"type":"string","in":"body"},{"name":"status","description":"Initial conversation status","required":false,"type":"string","in":"body"},{"name":"name","description":"Optional name for the conversation","required":false,"type":"string","in":"body"},{"name":"actor_id","description":"Actor ID to associate with this conversation","required":false,"type":"string","in":"body"},{"name":"retrieval","description":"Whether this conversation's turns are embedded for vector retrieval. Turns are stored and chunked either way, so `none` leaves them readable and reachable by full-text search without paying for an embedding. `null` (the default) inherits the project's `default_conversation_retrieval`.","required":false,"type":"string","in":"body"}]},"get-conversation":{serviceClass:"Conversations",operationId:"getConversation",description:"Returns a conversation by its ID",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"get",pathParams:["conversation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"}]},"update-conversation":{serviceClass:"Conversations",operationId:"updateConversation",description:"Updates the status of a conversation",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"patch",pathParams:["conversation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"},{"name":"status","description":"New conversation status","required":false,"type":"string","in":"body"},{"name":"name","description":"New conversation name","required":false,"type":"string","in":"body"},{"name":"retrieval","description":"Whether this conversation's turns are embedded for vector retrieval. Turns are stored and chunked either way, so `none` leaves them readable and reachable by full-text search without paying for an embedding. `null` (the default) inherits the project's `default_conversation_retrieval`. Switching it on embeds the turns already in the conversation, so the whole conversation becomes retrievable rather than only what is said next.","required":false,"type":"string","in":"body"}]},"delete-conversation":{serviceClass:"Conversations",operationId:"deleteConversation",description:"Deletes a conversation by its ID",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"delete",pathParams:["conversation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"}]},"list-conversation-messages":{serviceClass:"Conversations",operationId:"listConversationMessages",description:"Returns all messages (documents) attached to a conversation, ordered by position",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"get",pathParams:["conversation_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"add-conversation-message":{serviceClass:"Conversations",operationId:"addConversationMessage",description:"Creates a document from the message text and attaches it to the conversation at the given position. If position is omitted, it is appended at the end.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"post",pathParams:["conversation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"},{"name":"message","description":"Message text content to add to the conversation","required":true,"type":"string","in":"body"},{"name":"role","description":"Role of the message sender","required":true,"type":"string","in":"body"},{"name":"actor_id","description":"Optional actor ID to associate with this message (user identity)","required":false,"type":"string","in":"body"},{"name":"position","description":"Zero-based position. Defaults to MAX+1 (append).","required":false,"type":"integer","in":"body"},{"name":"metadata","description":"Caller-owned annotations on the message (e.g. a phone number, a channel), stored as sent and returned verbatim. The platform does not read the bag, so nothing in it reaches the model: a value the model should see belongs in `message`.","required":false,"type":"object","in":"body"}]},"generate-conversation-message":{serviceClass:"Conversations",operationId:"generateConversationMessage",description:"Generates the next message using the specified actor's linked agent or chat. Background by default: returns `202 Accepted` immediately and the reply lands as a new ConversationMessage when it completes — poll `GET /api/v1/conversations/{conversation_id}/messages` for it. Pass `?wait=true` to block and receive the result inline. On `completed`, the reply is persisted as a new ConversationMessage authored by that actor. On `requires_action`, nothing is persisted; the caller must submit tool outputs via the Agents module and re-invoke generate — so a flow using client tools should pass `?wait=true`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"post",pathParams:["conversation_id"],queryParams:["wait"],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"","required":true,"type":"string","in":"path"},{"name":"wait","description":"When omitted or `false` (default), the generation runs in the background and `202 Accepted` is returned immediately. Pass `true` to block until the generation settles and receive the result. A tool call, `builtin` or MCP, always waits.","required":false,"type":"boolean","in":"query"},{"name":"agent_id","description":"ID of the agent that will produce the next message.","required":true,"type":"string","in":"body"},{"name":"model","description":"Optional model override.","required":false,"type":"string","in":"body"},{"name":"stream","description":"If true, stream tokens via SSE. NOT IMPLEMENTED in v1 — returns 501.","required":false,"type":"boolean","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as `X-Soat-Context-<key>` headers on every `http`, `mcp` and `builtin` tool call in this generation. The header name is the deployment's configured context prefix (`X-Soat-Context-` by default) plus the key verbatim — no character is re-cased and keys are never case-converted, so they round-trip exactly as sent. An invalid or colliding key is rejected with `400 INVALID_TOOL_CONTEXT_KEY`.","required":false,"type":"object","in":"body"}]},"remove-conversation-message":{serviceClass:"Conversations",operationId:"removeConversationMessage",description:"Removes a document from a conversation",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"delete",pathParams:["conversation_id","document_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"},{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"}]},"get-conversation-tags":{serviceClass:"Conversations",operationId:"getConversationTags",description:"Returns all tags attached to the conversation",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"get",pathParams:["conversation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"}]},"replace-conversation-tags":{serviceClass:"Conversations",operationId:"replaceConversationTags",description:"Replaces all tags on the conversation with the provided tags",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"put",pathParams:["conversation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"merge-conversation-tags":{serviceClass:"Conversations",operationId:"mergeConversationTags",description:"Merges provided tags into the conversation's existing tags (existing tags are preserved unless overridden)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/conversations",httpMethod:"patch",pathParams:["conversation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"conversation_id","description":"Conversation ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"list-deciders":{serviceClass:"Deciders",operationId:"listDeciders",description:"Returns the deciders defined in a project, newest first.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-decider":{serviceClass:"Deciders",operationId:"createDecider",description:"Creates a decider at version 1: a named question set and the backend that answers it, exactly one of `agent_id` and `tool_id`. Every question declares a finite answer space, validated here. The backend must be in the decider's project. An agent must carry no tool surface — no tool binding left active by `active_tool_ids` and no `knowledge_config.write_memory_store_id` — or the create is refused with `400 DECIDER_AGENT_NOT_TOOL_LESS`. A tool must be `http` or `pipeline` and must not pin `input` or `questions` in its `preset_parameters`, or the create is refused with `400 DECIDER_TOOL_NOT_CALLABLE`. A duplicate `name` in the project is `409 NAME_CONFLICT`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"name","description":"Human-readable name, unique per project","required":true,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"agent_id","description":"A tool-less agent that answers","required":false,"type":"string","in":"body"},{"name":"tool_id","description":"An http or pipeline tool that answers","required":false,"type":"string","in":"body"},{"name":"questions","description":"The questions, in the order the model is shown them.","required":true,"type":"array","in":"body"},{"name":"version_label","description":"Optional tag for version 1, e.g. `initial`","required":false,"type":"string","in":"body"}]},"get-decider":{serviceClass:"Deciders",operationId:"getDecider",description:"Returns a decider with its current question set and version.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"get",pathParams:["decider_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"decider_id","description":"The decider ID","required":true,"type":"string","in":"path"}]},"update-decider":{serviceClass:"Deciders",operationId:"updateDecider",description:"Updates any of `name`, `description`, the backend and `questions`. Naming `agent_id` or `tool_id` replaces the current backend; naming both is `400`. Only a change to `questions` archives a new version; a rename, a new backend or a rewrite of the questions the decider already holds leaves `version` where it is. A new backend is held to the same rules as on create.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"patch",pathParams:["decider_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"decider_id","description":"The decider ID","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"agent_id","description":"Replaces the backend with this agent","required":false,"type":"string","in":"body"},{"name":"tool_id","description":"Replaces the backend with this tool","required":false,"type":"string","in":"body"},{"name":"questions","description":"The questions, in the order the model is shown them.","required":false,"type":"array","in":"body"},{"name":"version_label","description":"Optional tag for the version this write archives. Ignored when the write changes no question, since no version is archived.","required":false,"type":"string","in":"body"},{"name":"expected_version","description":"Refuses the write unless the decider is at this version.","required":false,"type":"integer","in":"body"}]},"delete-decider":{serviceClass:"Deciders",operationId:"deleteDecider",description:"Deletes a decider and its version archive. Its decisions remain and keep naming the decider's ID.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"delete",pathParams:["decider_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"decider_id","description":"The decider ID","required":true,"type":"string","in":"path"}]},"list-decider-versions":{serviceClass:"Deciders",operationId:"listDeciderVersions",description:"Returns the decider's archived question sets, newest first. A version is written on create and on every write that changes `questions`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"get",pathParams:["decider_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"decider_id","description":"The decider ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-decider-version":{serviceClass:"Deciders",operationId:"getDeciderVersion",description:"Returns the question set a version held. A decision names the `decider_version` it was answered under, so this is how its criteria are read after the decider has changed.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"get",pathParams:["decider_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"decider_id","description":"The decider ID","required":true,"type":"string","in":"path"},{"name":"version","description":"The archived version number","required":true,"type":"integer","in":"path"}]},"restore-decider-version":{serviceClass:"Deciders",operationId:"restoreDeciderVersion",description:"Writes an archived question set back as the decider's live one, which archives it again as a new version rather than rewinding the counter. Restoring the question set the decider already holds is a no-op.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"post",pathParams:["decider_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"decider_id","description":"The decider ID","required":true,"type":"string","in":"path"},{"name":"version","description":"The archived version number","required":true,"type":"integer","in":"path"},{"name":"label","description":"Optional tag for the new version. Defaults to `restored from vN`.","required":false,"type":"string","in":"body"}]},"create-decision":{serviceClass:"Deciders",operationId:"createDecision",description:"Evaluates the decider against `input` and records the decision. The questions come from the decider, never from the request, so a call site can only supply what is judged. To send the questions with the request, use `POST /api/v1/decisions`. Everything that can refuse the request is checked before the decision is written, so a refusal is a `4xx` and never a polled failure: the agent's tool surface (`400 DECIDER_AGENT_NOT_TOOL_LESS`), a tool that cannot answer (`400 DECIDER_TOOL_NOT_CALLABLE`), a paused project (`409 PROJECT_PAUSED`) and, for an agent, quota admission (`429 QUOTA_EXCEEDED`). A tool backend is called with `{ input, questions }` and must answer `{ answers: [{ name, probability | choice | score, probabilities?, confidence? }] }`, the request and response of OpenAI's Decisions API; an answer outside that contract fails the decision with `DECISION_ANSWER_INVALID`. With `wait: false`, the default, the answer is `201` with the decision `queued`; poll `GET /api/v1/decisions/{decision_id}` or subscribe to `decisions.completed` and `decisions.failed`. With `wait: true` it is `201` with the decision settled.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"post",pathParams:["decider_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"decider_id","description":"The decider ID","required":true,"type":"string","in":"path"},{"name":"input","description":"What is judged, not stored: a string; a list of `user` messages whose `content` is a string or `input_text` and `input_image` parts (an image as a base64 data URL); or any other JSON value, which an agent is shown as indented JSON. An array of objects carrying `role` is read as messages.","required":true,"type":"unknown","in":"body"},{"name":"metadata","description":"Caller-owned annotations stored on the decision and returned on every read — typically the id of what was judged, since the input itself is not stored. Written once, with the decision. A non-object is rejected with `400 VALIDATION_FAILED` and no decision is written.","required":false,"type":"object","in":"body"},{"name":"wait","description":"True evaluates before answering and returns the settled decision. False — the default — returns the `queued` decision at once. A tool call, `builtin` or MCP, always waits.","required":false,"type":"boolean","in":"body"}]},"list-decisions":{serviceClass:"Deciders",operationId:"listDecisions",description:"Returns the decisions in a project, newest first.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"get",pathParams:[],queryParams:["project_id","decider_id","status","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"decider_id","description":"Only decisions requested from this decider","required":false,"type":"string","in":"query"},{"name":"status","description":"Only decisions in this status","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-inline-decision":{serviceClass:"Deciders",operationId:"createInlineDecision",description:"Evaluates `questions` sent with the request against `input`, answered by the agent or tool the request names (exactly one of `agent_id` and `tool_id`, held to the same rules as a decider's backend), and records the decision with its questions. The request and answers are those of OpenAI's Decisions API, with the backend in place of `model`. No decider names the questions, so the decision carries them in `questions` and has no `decider_id` or `decider_version`. Admission, `wait` and the tool contract are those of `POST /api/v1/deciders/{decider_id}/decisions`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"agent_id","description":"A tool-less agent that answers","required":false,"type":"string","in":"body"},{"name":"tool_id","description":"An http or pipeline tool that answers","required":false,"type":"string","in":"body"},{"name":"questions","description":"The questions, in the order the model is shown them.","required":true,"type":"array","in":"body"},{"name":"input","description":"What is judged, not stored: a string; a list of `user` messages whose `content` is a string or `input_text` and `input_image` parts (an image as a base64 data URL); or any other JSON value, which an agent is shown as indented JSON. An array of objects carrying `role` is read as messages.","required":true,"type":"unknown","in":"body"},{"name":"metadata","description":"Caller-owned annotations stored on the decision. A non-object is rejected with `400 VALIDATION_FAILED`.","required":false,"type":"object","in":"body"},{"name":"wait","description":"True evaluates before answering and returns the settled decision. False — the default — returns the `queued` decision at once. A tool call, `builtin` or MCP, always waits.","required":false,"type":"boolean","in":"body"}]},"get-decision":{serviceClass:"Deciders",operationId:"getDecision",description:"Returns a decision. Poll it after a `wait: false` request until `status` is `completed` or `failed`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/deciders",httpMethod:"get",pathParams:["decision_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"decision_id","description":"The decision ID","required":true,"type":"string","in":"path"}]},"list-documents":{serviceClass:"Documents",operationId:"listDocuments",description:"Returns all documents the caller has access to. If projectId is provided, returns only documents in that project. project keys are scoped to a single project automatically. JWT users without projectId receive documents across all their accessible projects.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"get",pathParams:[],queryParams:["project_id","path_prefix","include_withdrawn","related_to","tags","metadata","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (optional)","required":false,"type":"string","in":"query"},{"name":"path_prefix","description":"Only documents filed under this directory. The prefix is a path boundary, not a substring: `/reports` returns `/reports/q1.txt` and never `/reports-archive/q1.txt`, and `/` selects the whole project. A leading slash is optional and a trailing one is ignored, so `reports`, `/reports` and `/reports/` are the same filter. `%` and `_` are literal characters, not wildcards.","required":false,"type":"string","in":"query"},{"name":"include_withdrawn","description":"Include withdrawn documents. A withdrawn document leaves every default read and is not in the knowledge index at all — its chunks are dropped when it is withdrawn — so this shows it in the listing but never in a search.","required":false,"type":"boolean","in":"query"},{"name":"related_to","description":"Only documents related to this one, on either side of the edge: what it points at, and what points at it. An id with no relations narrows the listing to nothing.","required":false,"type":"string","in":"query"},{"name":"tags","description":"Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n","required":false,"type":"array","in":"query"},{"name":"metadata","description":"A `MetadataFilter` as JSON, url-encoded. It travels as JSON rather than as `key:value` pairs because the match is exact and a query string cannot otherwise say whether `3` is the number or the string.","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-document":{serviceClass:"Documents",operationId:"createDocument",description:"Creates a new text document and generates an embedding vector for semantic search. project keys automatically infer the project from the key's scope; JWT callers must supply projectId.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID. Required for JWT auth; omit when using an project key.","required":false,"type":"string","in":"body"},{"name":"content","description":"","required":true,"type":"string","in":"body"},{"name":"path","description":"Logical path within the project (e.g. /reports/q1.txt). Defaults to `/<filename>`, or to `/<document_id>.txt` when neither is given — a document with no path is reachable only by its id, since a prefix filter never matches null.","required":false,"type":"string","in":"body"},{"name":"filename","description":"","required":false,"type":"string","in":"body"},{"name":"title","description":"Document title","required":false,"type":"string","in":"body"},{"name":"metadata","description":"Arbitrary metadata object. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.","required":false,"type":"object","in":"body"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":false,"type":"object","in":"body"},{"name":"chunk_strategy","description":"How to split the content into embeddable chunks. `whole` (default) stores the content as a single chunk; `size` splits into fixed-size character windows with overlap. `page` is equivalent to `whole` for plain text.","required":false,"type":"string","in":"body"},{"name":"chunk_size","description":"Window size in characters when `chunk_strategy=size`. Defaults to 1000.","required":false,"type":"integer","in":"body"},{"name":"chunk_overlap","description":"Overlap in characters between consecutive windows when `chunk_strategy=size`. Defaults to 200.","required":false,"type":"integer","in":"body"}]},"ingest-document":{serviceClass:"Documents",operationId:"ingestDocument",description:"Parses an already-uploaded file and creates one Document split into one or more embedded chunks. The source format is detected from the file's content type: PDFs are parsed page-by-page; `text/plain` and `text/markdown` files are read as a single source. How the source is chunked is controlled by `chunk_strategy`. A file can only back one Document — a second call with the same `file_id` returns `409 FILE_ALREADY_INGESTED`. To re-process an already-ingested file (e.g. with a different `chunk_strategy`), use `POST /documents/{document_id}/ingest`; to ingest the same source under a different path, upload a new copy of the file first.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"post",pathParams:[],queryParams:["wait"],headerParams:[],cookieParams:[],flags:[{"name":"wait","description":"When omitted or `false` (default), processing runs in the background and `202 Accepted` is returned immediately with `status=pending`. Pass `true` to block until processing completes and receive `201 Created` with `status=ready`.","required":false,"type":"boolean","in":"query"},{"name":"file_id","description":"ID of the uploaded file. Must be one of application/pdf, text/plain, text/markdown.","required":true,"type":"string","in":"body"},{"name":"project_id","description":"Project ID. Required for JWT auth; omit when using a project key.","required":false,"type":"string","in":"body"},{"name":"path_prefix","description":"Path prefix under which to store the document (e.g. /docs/). The filename is appended automatically.","required":false,"type":"string","in":"body"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":false,"type":"object","in":"body"},{"name":"chunk_strategy","description":"How to split the source into chunks. `page` (default) creates one chunk per non-empty page (PDF); for non-paged sources it yields a single chunk. `whole` joins everything into one chunk. `size` splits into fixed-size character windows with overlap.","required":false,"type":"string","in":"body"},{"name":"chunk_size","description":"Window size in characters when `chunk_strategy=size`. Defaults to 1000.","required":false,"type":"integer","in":"body"},{"name":"chunk_overlap","description":"Overlap in characters between consecutive windows when `chunk_strategy=size`. Defaults to 200.","required":false,"type":"integer","in":"body"}]},"export-documents":{serviceClass:"Documents",operationId:"exportDocuments",description:"Streams a project's documents as newline-delimited JSON — one document object per line, oldest first — for archiving the corpus or shipping it into another system. `project_id` is required: the export is per-project by design. The rows are the rows the listing returns for the same caller, so a policy that hides a document hides it here too, and withdrawn documents and the reserved `/.system/` root are left out.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"get",pathParams:[],queryParams:["project_id","path_prefix"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project whose documents are exported","required":true,"type":"string","in":"query"},{"name":"path_prefix","description":"Only documents filed under this directory. The prefix is a path boundary, not a substring, exactly as on the listing.","required":false,"type":"string","in":"query"}]},"get-document":{serviceClass:"Documents",operationId:"getDocument",description:"Returns a document with its text content",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"get",pathParams:["document_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"}]},"update-document":{serviceClass:"Documents",operationId:"updateDocument",description:"Updates document content, title, path, metadata, or tags. Supplying `path` moves the document to a new logical path within the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"patch",pathParams:["document_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"content","description":"New text content","required":false,"type":"string","in":"body"},{"name":"title","description":"New title","required":false,"type":"string","in":"body"},{"name":"path","description":"Logical path within the project (e.g. /reports/q1.txt). Pass null to clear.","required":false,"type":"string","in":"body"},{"name":"metadata","description":"Arbitrary metadata object, replacing the stored bag; `null` clears it. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.","required":false,"type":"object","in":"body"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":false,"type":"object","in":"body"},{"name":"expected_version","description":"Refuses the write unless the document is at this version.","required":false,"type":"integer","in":"body"}]},"delete-document":{serviceClass:"Documents",operationId:"deleteDocument",description:"Deletes a document and its underlying file",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"delete",pathParams:["document_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"}]},"list-document-relations":{serviceClass:"Documents",operationId:"listDocumentRelations",description:"Returns the typed edges this document asserts about others, oldest first. An edge is owned by the document it leaves, so this is what the document claims — use `?related_to=` on the listing to find what claims something about it.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"get",pathParams:["document_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-document-relation":{serviceClass:"Documents",operationId:"createDocumentRelation",description:"Asserts a typed edge from this document to another in the same project. Asserting the same edge twice is `409`: an edge is a fact, and the second assertion is the same fact. Writing an edge is a write of the asserting document (`documents:UpdateDocument`); the document it points at is unchanged, which is what lets an agent record what its own report derives from without being able to make another report claim anything.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"post",pathParams:["document_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"","required":true,"type":"string","in":"path"},{"name":"type","description":"What this document claims about the other","required":true,"type":"string","in":"body"},{"name":"to_document_id","description":"The document the edge points at. Must be in the same project, and visible to the caller.","required":true,"type":"string","in":"body"}]},"delete-document-relation":{serviceClass:"Documents",operationId:"deleteDocumentRelation",description:"Removes an edge this document asserts. Both documents are left as they are — retracting a claim is not a change to what it was about.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"delete",pathParams:["document_id","relation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"","required":true,"type":"string","in":"path"},{"name":"relation_id","description":"","required":true,"type":"string","in":"path"}]},"get-document-status":{serviceClass:"Documents",operationId:"getDocumentStatus",description:"Returns a lightweight ingestion status payload for polling — `status`, `chunk_count`, `total_pages`, and (when failed) `error`. Unlike `GET /documents/{document_id}`, it never returns the assembled chunk content, so it is cheap to poll on large documents. A document whose ingestion has stalled (no progress past the configured timeout) is transitioned to `failed` with `error=INGESTION_TIMEOUT` on read.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"get",pathParams:["document_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"}]},"withdraw-document":{serviceClass:"Documents",operationId:"withdrawDocument",description:"Takes a document out of every default read while keeping its history. The withdrawal is archived as a **tombstone version**, so one mechanism answers what a document holds now and there is no second lifecycle flag for a reader to miss. Its chunks are dropped from the knowledge index, so a withdrawn document costs a live search nothing. The content is not lost with them: it is in the version before the tombstone, which [`POST /api/v1/documents/{document_id}/versions/{version}/restore`](/docs/api/documents/restore-document-version) re-chunks from. `DELETE` stays what it is — permanent, and it removes the backing file. Withdrawal is the reversible act. It does not apply under `/.system/`: a platform-written document keeps the owning module's lifecycle.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"post",pathParams:["document_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"document_id","description":"","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"version_label","description":"Optional tag for the tombstone version this write archives.","required":false,"type":"string","in":"body"},{"name":"expected_version","description":"Refuses the withdrawal unless the document is at this version.","required":false,"type":"integer","in":"body"}]},"list-document-versions":{serviceClass:"Documents",operationId:"listDocumentVersions",description:"Returns the document's archived states, newest first. A version is written on create and on every subsequent write that changes the content or its annotations, and a withdrawal is archived as a version carrying no content.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"get",pathParams:["document_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-document-version":{serviceClass:"Documents",operationId:"getDocumentVersion",description:"Returns the exact content and annotations the document held at a given version, which is what lets a run that cited a version read what it read.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"get",pathParams:["document_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"","required":true,"type":"string","in":"path"},{"name":"version","description":"","required":true,"type":"integer","in":"path"}]},"restore-document-version":{serviceClass:"Documents",operationId:"restoreDocumentVersion",description:"Writes an archived version's content and annotations back as the document's live state, which archives them again as a **new** version rather than rewinding the counter — so a run citing any version in between still resolves. The restore runs through the ordinary update path, so the content is re-chunked and re-embedded; restoring the state the document already holds is a no-op and archives nothing. Restoring any content version of a withdrawn document brings it back into listings and search. A tombstone version has no content, so naming one is `400 VALIDATION_FAILED`: restore the version before it instead.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"post",pathParams:["document_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"","required":true,"type":"string","in":"path"},{"name":"version","description":"","required":true,"type":"integer","in":"path"},{"name":"label","description":"Optional tag for the version this restore archives. Defaults to `restored from v<version>`.","required":false,"type":"string","in":"body"}]},"reingest-document":{serviceClass:"Documents",operationId:"reingestDocument",description:"Re-runs ingestion for an existing document against its already-stored source file. Existing chunks are discarded and the document is reset to `status=pending` before re-processing. Use this to recover a document stuck in `processing`/`failed` or to re-chunk with a different strategy without re-uploading the file. Background by default (`202`); pass `?wait=true` to run synchronously (`201`).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"post",pathParams:["document_id"],queryParams:["wait"],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"},{"name":"wait","description":"When omitted or `false` (default), processing runs in the background and `202 Accepted` is returned immediately with `status=pending`. Pass `true` to block until processing completes and receive `201 Created` with `status=ready`.","required":false,"type":"boolean","in":"query"},{"name":"chunk_strategy","description":"How to split the source into chunks. Defaults to `page`.","required":false,"type":"string","in":"body"},{"name":"chunk_size","description":"Window size in characters when `chunk_strategy=size`. Defaults to 1000.","required":false,"type":"integer","in":"body"},{"name":"chunk_overlap","description":"Overlap in characters between consecutive windows when `chunk_strategy=size`. Defaults to 200.","required":false,"type":"integer","in":"body"}]},"complete-ingestion-callback":{serviceClass:"Documents",operationId:"completeIngestionCallback",description:"Token-authed callback for a tool converter that deferred conversion by returning `{ \"status\": \"pending\" }` (see the Ingestion Rules module docs). Not IAM-gated — the external converter is not a SOAT principal, so it authenticates with the single-use token minted for this document and ingestion attempt (delivered as `callback.token` / embedded in `callback.url` in the original converter invocation). Accepted only while the document is still awaiting that exact attempt; rejected with `409` if the attempt already completed, timed out, or was superseded by a re-ingest.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"post",pathParams:["document_id"],queryParams:["token"],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"},{"name":"token","description":"Single-use signed token from the original `callback.token`","required":true,"type":"string","in":"query"},{"name":"text","description":"","required":false,"type":"string","in":"body"},{"name":"pages","description":"","required":false,"type":"array","in":"body"}]},"get-document-tags":{serviceClass:"Documents",operationId:"getDocumentTags",description:"Returns all tags attached to the document",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"get",pathParams:["document_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"}]},"replace-document-tags":{serviceClass:"Documents",operationId:"replaceDocumentTags",description:"Replaces all tags on the document with the provided tags (not merged)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"put",pathParams:["document_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"merge-document-tags":{serviceClass:"Documents",operationId:"mergeDocumentTags",description:"Merges provided tags into the document's existing tags (existing tags are preserved unless overridden)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/documents",httpMethod:"patch",pathParams:["document_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"document_id","description":"Document ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"create-embeddings":{serviceClass:"Embeddings",operationId:"createEmbeddings",description:"Generates embedding vectors for one or more text inputs using the server's configured embedding model. Provide `input` for a single text or `inputs` for a batch. At least one is required. Returns `embedding` when `input` is used, and `embeddings` when `inputs` is used. `project_id` names the project the call's token usage is metered against, so embedding spend reaches the usage rollup and the project's `cost_usd` / `tokens` quotas. A project-scoped credential supplies its own project; a call that names none and is bound to none is served but not metered.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/embeddings",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project the call's usage is billed to. Optional; a project-scoped credential supplies its own. Requires write access to the named project.","required":false,"type":"string","in":"body"},{"name":"input","description":"Single text to embed.","required":false,"type":"string","in":"body"},{"name":"inputs","description":"Batch of texts to embed. At most 256 per request — each input is one call to the embedding model, so a larger batch is refused (`VALIDATION_FAILED`) rather than queued. Split it across requests.","required":false,"type":"array","in":"body"}]},"list-datasets":{serviceClass:"Evaluations",operationId:"listDatasets",description:"Returns the datasets defined in a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-dataset":{serviceClass:"Evaluations",operationId:"createDataset",description:"Creates a project-scoped dataset — a named collection of test cases an eval runs an agent against. Names are unique per project. Datasets are operator-owned **fixtures**. The platform's content purge never deletes or mutates a dataset item, so erasing a generation cannot silently stop a test suite from being runnable.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"name","description":"Unique name within the project","required":true,"type":"string","in":"body"},{"name":"description","description":"What this suite covers","required":false,"type":"string","in":"body"}]},"get-dataset":{serviceClass:"Evaluations",operationId:"getDataset",description:"Returns a specific dataset",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"get",pathParams:["dataset_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"dataset_id","description":"Dataset ID","required":true,"type":"string","in":"path"}]},"update-dataset":{serviceClass:"Evaluations",operationId:"updateDataset",description:"Updates a dataset's name and/or description",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"put",pathParams:["dataset_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"dataset_id","description":"Dataset ID","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"}]},"delete-dataset":{serviceClass:"Evaluations",operationId:"deleteDataset",description:"Deletes a dataset, its items, and every eval bound to it. Results of runs that already scored those items keep their frozen copies of the input and expected output.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"delete",pathParams:["dataset_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"dataset_id","description":"Dataset ID","required":true,"type":"string","in":"path"}]},"list-dataset-items":{serviceClass:"Evaluations",operationId:"listDatasetItems",description:"Returns the test cases in a dataset, oldest first",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"get",pathParams:["dataset_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"dataset_id","description":"Dataset ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-dataset-item":{serviceClass:"Evaluations",operationId:"createDatasetItem",description:"Adds one test case. `input` is replayed verbatim as the generation's messages, so it must be a non-empty array of `{ role, content }`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"post",pathParams:["dataset_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"dataset_id","description":"Dataset ID","required":true,"type":"string","in":"path"},{"name":"input","description":"Messages replayed verbatim as the generation's input","required":true,"type":"array","in":"body"},{"name":"expected_output","description":"Reference answer for exact_match / embedding_similarity / llm_judge scorers","required":false,"type":"string","in":"body"},{"name":"metadata","description":"Free-form tags, opaque to the platform","required":false,"type":"object","in":"body"}]},"create-dataset-item-from-generation":{serviceClass:"Evaluations",operationId:"createDatasetItemFromGeneration",description:"Promotes a real, completed generation into a test case: its input messages become the item's `input`, and its own answer becomes `expected_output` unless you supply one. Use it to build an evaluation set out of production traffic rather than hand-authoring fixtures. The item is a **copy**, not a view. It keeps working after the source generation's content is purged, and `source_generation_id` goes null if that generation is deleted — a purge can never quietly stop a suite from being runnable. Requires both `evaluations:CreateDataset` and `generations:GetGeneration`: the call copies content out of a generation, so a principal that may not read that generation may not curate it either. Only a **completed** generation can be promoted (`409 GENERATION_NOT_COMPLETED`), and only while its content is still available: an agent or project running with `trace_content_mode: none` never stored the input, and a purged or expired generation no longer has it (`409 GENERATION_CONTENT_UNAVAILABLE`). Generations that predate input recording answer the same way.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"post",pathParams:["dataset_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"dataset_id","description":"Dataset ID","required":true,"type":"string","in":"path"},{"name":"generation_id","description":"The completed generation to promote. Must belong to the same project as the dataset.","required":true,"type":"string","in":"body"},{"name":"expected_output","description":"Reference answer. Omit to use the generation's own answer; pass `null` to store the item with no reference answer.","required":false,"type":"string","in":"body"},{"name":"metadata","description":"Free-form tags, opaque to the platform","required":false,"type":"object","in":"body"}]},"update-dataset-item":{serviceClass:"Evaluations",operationId:"updateDatasetItem",description:"Updates a test case. Runs that already scored it are unaffected — each result carries its own frozen copy of the input and expected output.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"put",pathParams:["dataset_id","item_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"dataset_id","description":"Dataset ID","required":true,"type":"string","in":"path"},{"name":"item_id","description":"Dataset item ID","required":true,"type":"string","in":"path"},{"name":"input","description":"Messages replayed verbatim as the generation's input","required":false,"type":"array","in":"body"},{"name":"expected_output","description":"","required":false,"type":"string","in":"body"},{"name":"metadata","description":"","required":false,"type":"object","in":"body"}]},"delete-dataset-item":{serviceClass:"Evaluations",operationId:"deleteDatasetItem",description:"Deletes a test case. Results of runs that already scored it stay readable; their `dataset_item_id` becomes null.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"delete",pathParams:["dataset_id","item_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"dataset_id","description":"Dataset ID","required":true,"type":"string","in":"path"},{"name":"item_id","description":"Dataset item ID","required":true,"type":"string","in":"path"}]},"list-evals":{serviceClass:"Evaluations",operationId:"listEvals",description:"Returns the evals defined in a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-eval":{serviceClass:"Evaluations",operationId:"createEval",description:"Binds an agent under test to a dataset and a list of scorers. The agent and the dataset must belong to the same project as the eval; a cross-project reference is rejected with 400. Scorer config is frozen here rather than read from the agent at run time, so two runs of the same eval are always judged by the same criteria and their comparison measures the agent instead of the config drifting underneath it. Each scorer `type` may appear at most once.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"name","description":"Unique name within the project","required":true,"type":"string","in":"body"},{"name":"agent_id","description":"The agent under test","required":true,"type":"string","in":"body"},{"name":"dataset_id","description":"The dataset to run it against","required":true,"type":"string","in":"body"},{"name":"scorers","description":"Scorer configs, a discriminated union on `type`. Each type may appear at most once. Every scorer produces `{ score: 0–1, passed: boolean }`; binary scorers emit 0 or 1.\n\n`exact_match` compares the trimmed output text to `expected_output`. `contains` looks for `value` in the output text. `json_logic` evaluates `expression` over `{ input, output, object, expected, item.metadata }`, where `object` is the structured output (absent when the agent has no `output_schema`). `output_schema` validates the structured output against the scorer's own `schema`, falling back to the agent's; it requires the agent to carry an `output_schema`, because without one the platform emits no structured output and every item would score 0.\n\n`llm_judge` grades the output with a model completion, returning a continuous score plus its `reasoning`. Its `pass_threshold` is required: a continuous score says nothing about where \"good enough\" is, and a defaulted cutoff would silently decide the gate.\n\n`embedding_similarity` embeds the output text and `expected_output` with the platform's configured embedding model (`EMBEDDING_PROVIDER` / `EMBEDDING_MODEL` — the same stack document ingestion uses) and scores their cosine similarity, clamped to 0-1. Its `pass_threshold` is required for the same reason as the judge's. An item without an `expected_output` scores 0; an embedding backend failure marks the **item** errored, never a score of 0.\n\n`tool` runs a custom scoring algorithm: a server-callable project tool the engine invokes once per item with the item's context. Unlike the built-in types it may appear several times, each under a distinct `name` — outcomes and aggregates key on the name.\n\n`decider` grades each item with a decision of a project decider: its `score` expression reads the decision's `answers`. Like `tool`, it keys on its `name` and may appear several times.","required":true,"type":"array","in":"body"},{"name":"pass_threshold","description":"0–1. The run passes iff its pass rate — passed items over non-errored items — is at least this. Null reports scores without gating on them.","required":false,"type":"number","in":"body"},{"name":"group_by","description":"A key of the items' `metadata`. A run rolls its scores up per string value of that key in `aggregate_scores.grouping`. Null reports no grouping.","required":false,"type":"string","in":"body"}]},"get-eval":{serviceClass:"Evaluations",operationId:"getEval",description:"Returns a specific eval",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"get",pathParams:["eval_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"eval_id","description":"Eval ID","required":true,"type":"string","in":"path"}]},"update-eval":{serviceClass:"Evaluations",operationId:"updateEval",description:"Updates an eval. Changing `agent_id` re-validates the scorers against the new agent, since an `output_schema` scorer that was legal against the old one may not be.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"put",pathParams:["eval_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"eval_id","description":"Eval ID","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"agent_id","description":"","required":false,"type":"string","in":"body"},{"name":"dataset_id","description":"","required":false,"type":"string","in":"body"},{"name":"scorers","description":"Scorer configs, a discriminated union on `type`. Each type may appear at most once. Every scorer produces `{ score: 0–1, passed: boolean }`; binary scorers emit 0 or 1.\n\n`exact_match` compares the trimmed output text to `expected_output`. `contains` looks for `value` in the output text. `json_logic` evaluates `expression` over `{ input, output, object, expected, item.metadata }`, where `object` is the structured output (absent when the agent has no `output_schema`). `output_schema` validates the structured output against the scorer's own `schema`, falling back to the agent's; it requires the agent to carry an `output_schema`, because without one the platform emits no structured output and every item would score 0.\n\n`llm_judge` grades the output with a model completion, returning a continuous score plus its `reasoning`. Its `pass_threshold` is required: a continuous score says nothing about where \"good enough\" is, and a defaulted cutoff would silently decide the gate.\n\n`embedding_similarity` embeds the output text and `expected_output` with the platform's configured embedding model (`EMBEDDING_PROVIDER` / `EMBEDDING_MODEL` — the same stack document ingestion uses) and scores their cosine similarity, clamped to 0-1. Its `pass_threshold` is required for the same reason as the judge's. An item without an `expected_output` scores 0; an embedding backend failure marks the **item** errored, never a score of 0.\n\n`tool` runs a custom scoring algorithm: a server-callable project tool the engine invokes once per item with the item's context. Unlike the built-in types it may appear several times, each under a distinct `name` — outcomes and aggregates key on the name.\n\n`decider` grades each item with a decision of a project decider: its `score` expression reads the decision's `answers`. Like `tool`, it keys on its `name` and may appear several times.","required":false,"type":"array","in":"body"},{"name":"pass_threshold","description":"","required":false,"type":"number","in":"body"},{"name":"group_by","description":"A key of the items' `metadata` to group run scores by; null clears it, omitting it leaves it unchanged.","required":false,"type":"string","in":"body"}]},"delete-eval":{serviceClass:"Evaluations",operationId:"deleteEval",description:"Deletes an eval, its runs, and their results",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"delete",pathParams:["eval_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"eval_id","description":"Eval ID","required":true,"type":"string","in":"path"}]},"list-eval-runs":{serviceClass:"Evaluations",operationId:"listEvalRuns",description:"Returns an eval's runs, newest first",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"get",pathParams:["eval_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"eval_id","description":"Eval ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"start-eval-run":{serviceClass:"Evaluations",operationId:"startEvalRun",description:"Runs the eval against its dataset, creating one real agent generation per item and scoring the outputs. `wait: true` executes the run synchronously and returns it terminal, with its scores. The dataset is capped at 25 items for a synchronous run; a larger one is rejected with 400 rather than partially scored. `wait: false` (the default) enqueues one task per item and returns immediately with `status: \"queued\"`. A worker executes the items and the run settles itself; poll `GET /evals/{eval_id}/runs/{eval_run_id}` for the terminal status, or subscribe to the `eval_run.completed` webhook. There is no item cap on a queued run. The whole run is pinned to **one** agent version, stamped on `agent_version`: pass one explicitly to evaluate a canary before promoting it, or omit it to use the active release's stable version (or the live draft when no release is in effect). Without the pin, release assignment would bucket each item independently and blend two configs into a single score. With `baseline_run_id`, the finished run's `aggregate_scores.baseline` carries per-scorer deltas against that run, computed over the items present and scorable in **both** runs, with the divergence counted. A delta over a shifted dataset is therefore never presented as a clean comparison.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"post",pathParams:["eval_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"eval_id","description":"Eval ID","required":true,"type":"string","in":"path"},{"name":"wait","description":"True runs the eval synchronously (25-item cap) and returns a terminal run with its scores. False — the default — enqueues the items and returns a `queued` run immediately.","required":false,"type":"boolean","in":"body"},{"name":"agent_version","description":"An archived agent version to evaluate. Defaults to the active release's stable version, or the live draft version when no release is in effect.","required":false,"type":"integer","in":"body"},{"name":"baseline_run_id","description":"A terminal run of the same eval to compare against. The finished run's `aggregate_scores.baseline` reports per-scorer deltas over the item intersection. A run of a different eval is rejected with 400.","required":false,"type":"string","in":"body"},{"name":"metadata","description":"Caller-supplied key/value metadata attached to the run record for attribution — what this measurement was of (the commit or release candidate being scored, the CI job that asked for it). Round-trips verbatim on every read of the run, the list included.\n\nThe bag is caller-owned and no key is reserved: everything the platform decides about a run (`status`, `agent_version`, `baseline_run_id`, `aggregate_scores`, `passed`, the counts) is a field of its own and cannot be written from here. Nothing in the scoring path reads it. A non-object is rejected with `400 VALIDATION_FAILED` and no run is created.","required":false,"type":"object","in":"body"},{"name":"tool_context","description":"Key/value context forwarded to every item's generation, so an agent whose tools authorize through `tool_context` is scored against the configuration it runs in production rather than with an empty bag. Each key is forwarded as one `X-Soat-Context-<key>` header and resolves any `{{context:<key>}}` token in a bound tool's headers or `preset_parameters`.\n\nStored on the run and re-read per item, since a queued run (the default) is driven by a worker with no request behind it. **Write-only**: no read of the run returns it, unlike `metadata` — a run is a report other people read, and a credential in it is not theirs to see. Cleared once the run reaches a terminal state.\n\nAn eval generation has no session, so the reserved keys `session_id`, `actor_id` and `actor_external_id` are dropped (in any casing) rather than forwarded. Every other key becomes an HTTP header name and must match that grammar, or the request is rejected with `400 INVALID_TOOL_CONTEXT_KEY` and no run is created.","required":false,"type":"object","in":"body"}]},"get-eval-run":{serviceClass:"Evaluations",operationId:"getEvalRun",description:"Returns a run's status, counts, and aggregate scores",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"get",pathParams:["eval_id","eval_run_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"eval_id","description":"Eval ID","required":true,"type":"string","in":"path"},{"name":"eval_run_id","description":"Eval run ID","required":true,"type":"string","in":"path"}]},"list-eval-results":{serviceClass:"Evaluations",operationId:"listEvalResults",description:"Returns the per-item results of a run, oldest first",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"get",pathParams:["eval_id","eval_run_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"eval_id","description":"Eval ID","required":true,"type":"string","in":"path"},{"name":"eval_run_id","description":"Eval run ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"cancel-eval-run":{serviceClass:"Evaluations",operationId:"cancelEvalRun",description:"Cancels a queued or running run: its outstanding item tasks are dropped so it stops consuming provider budget, and the run settles as `canceled`. Results already written are kept — they are real measurements of generations that were really paid for — and `completed_count` / `errored_count` report what ran. `aggregate_scores` is deliberately left null: a partial roll-up in the same field a completed run uses would read as a whole-dataset verdict. A run that has already finished is rejected with 400.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/evaluations",httpMethod:"post",pathParams:["eval_id","eval_run_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"eval_id","description":"Eval ID","required":true,"type":"string","in":"path"},{"name":"eval_run_id","description":"Eval run ID","required":true,"type":"string","in":"path"}]},"list-exceptions":{serviceClass:"Exceptions",operationId:"listExceptions",description:"Returns exception items for a project, filterable by status, severity, and kind.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/exceptions",httpMethod:"get",pathParams:[],queryParams:["project_id","status","severity","kind","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"status","description":"Filter by triage status","required":false,"type":"string","in":"query"},{"name":"severity","description":"Filter by severity","required":false,"type":"string","in":"query"},{"name":"kind","description":"Filter by how the exception was filed","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-exception":{serviceClass:"Exceptions",operationId:"getException",description:"Returns a single exception item with its full detail.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/exceptions",httpMethod:"get",pathParams:["exception_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"exception_id","description":"Exception item ID","required":true,"type":"string","in":"path"}]},"acknowledge-exception":{serviceClass:"Exceptions",operationId:"acknowledgeException",description:"Moves the item to `acknowledged` (\"someone is on it\"), recording who. A no-op that returns the item unchanged when already acknowledged; rejected when already resolved.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/exceptions",httpMethod:"post",pathParams:["exception_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"exception_id","description":"Exception item ID","required":true,"type":"string","in":"path"}]},"resolve-exception":{serviceClass:"Exceptions",operationId:"resolveException",description:"Moves the item to `resolved` (\"fixed\"), recording who and an optional note.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/exceptions",httpMethod:"post",pathParams:["exception_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"exception_id","description":"Exception item ID","required":true,"type":"string","in":"path"},{"name":"note","description":"Optional resolution note","required":false,"type":"string","in":"body"}]},"list-files":{serviceClass:"Files",operationId:"listFiles",description:"Returns a list of all stored files",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"get",pathParams:[],queryParams:["project_id","path_prefix","tags","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Filter files by project ID","required":false,"type":"string","in":"query"},{"name":"path_prefix","description":"Only files under this directory. The prefix is a path boundary, not a substring: `/reports` returns `/reports/q1.txt` and never `/reports-archive/q1.txt`, and `/` selects the whole project. A leading slash is optional and a trailing one is ignored, so `reports`, `/reports` and `/reports/` are the same filter. `%` and `_` are literal characters, not wildcards. Naming a directory under `/.system/` is what includes platform-written files, which a list without this parameter leaves out.","required":false,"type":"string","in":"query"},{"name":"tags","description":"Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n","required":false,"type":"array","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-file":{serviceClass:"Files",operationId:"createFile",description:"Creates a new file record in the system",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project. Optional when authenticating with a project-scoped API key, which defaults to the key's project; required otherwise.","required":false,"type":"string","in":"body"},{"name":"prefix","description":"Directory within the project (e.g. /images). Optional; defaults to / (root). Combined with filename to form the file's key (path).","required":false,"type":"string","in":"body"},{"name":"filename","description":"Original / download name and the key's leaf segment (e.g. logo.png).","required":false,"type":"string","in":"body"},{"name":"content_type","description":"MIME type of the file","required":false,"type":"string","in":"body"},{"name":"size","description":"File size in bytes","required":false,"type":"integer","in":"body"},{"name":"metadata","description":"Caller-owned annotations on a resource, stored as the object they were written as: the types a value was written with are the types a read returns, so a filter can ask an ordering question about a number. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.\n\nNo key is reserved, and that is the point: every piece of state the platform owns lives in its own typed column, so nothing written here reaches platform state. The platform never reads the bag — it is not an IAM context, not a policy input and not part of a prompt — which is what separates it from a tag bag.","required":false,"type":"object","in":"body"}]},"upload-file":{serviceClass:"Files",operationId:"uploadFile",description:"Uploads a file to the server and stores it in the configured storage directory",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[]},"upload-file-base64":{serviceClass:"Files",operationId:"uploadFileBase64",description:"Uploads a file to the server using base64-encoded content",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project","required":false,"type":"string","in":"body"},{"name":"content","description":"Base64-encoded file content","required":true,"type":"string","in":"body"},{"name":"prefix","description":"Directory within the project (e.g. /documents). Optional; defaults to / (root).","required":false,"type":"string","in":"body"},{"name":"filename","description":"Original / download name and the key's leaf segment.","required":false,"type":"string","in":"body"},{"name":"content_type","description":"MIME type of the file","required":false,"type":"string","in":"body"},{"name":"metadata","description":"Caller-owned annotations on a resource, stored as the object they were written as: the types a value was written with are the types a read returns, so a filter can ask an ordering question about a number. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.\n\nNo key is reserved, and that is the point: every piece of state the platform owns lives in its own typed column, so nothing written here reaches platform state. The platform never reads the bag — it is not an IAM context, not a policy input and not part of a prompt — which is what separates it from a tag bag.","required":false,"type":"object","in":"body"}]},"create-presigned-url":{serviceClass:"Files",operationId:"createPresignedUrl",description:"Creates a short-lived, single-use presigned upload URL — the local-storage equivalent of an S3 presigned URL. The client then uploads the file content directly to the returned `upload_url` via `POST /api/v1/files/upload/{token}`, bypassing MCP payload size limits. When the server is configured with `SOAT_BASE_URL`, `upload_url` is a fully-qualified absolute URL so MCP agents and other clients can POST to it without knowing the server base URL in advance.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project","required":true,"type":"string","in":"body"},{"name":"prefix","description":"Directory within the project (e.g. /documents). Optional; defaults to / (root).","required":false,"type":"string","in":"body"},{"name":"filename","description":"Original / download name and the key's leaf segment.","required":false,"type":"string","in":"body"},{"name":"content_type","description":"MIME type of the file to be uploaded","required":false,"type":"string","in":"body"}]},"upload-file-with-token":{serviceClass:"Files",operationId:"uploadFileWithToken",description:"Uploads file content authorized by a single-use token from `POST /api/v1/files/presigned-url`. No bearer credential is required — the token is the credential. Accepts either multipart/form-data (field `file`) or JSON with a base64-encoded `content` field.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"post",pathParams:["token"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"token","description":"The single-use upload token (e.g. upt_...)","required":true,"type":"string","in":"path"},{"name":"content","description":"Base64-encoded file content (alternative to multipart `file`)","required":false,"type":"string","in":"body"},{"name":"filename","description":"Original / download name (overrides the token's filename)","required":false,"type":"string","in":"body"},{"name":"content_type","description":"MIME type of the file (overrides the token's content type)","required":false,"type":"string","in":"body"},{"name":"metadata","description":"Caller-owned annotations on a resource, stored as the object they were written as: the types a value was written with are the types a read returns, so a filter can ask an ordering question about a number. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.\n\nNo key is reserved, and that is the point: every piece of state the platform owns lives in its own typed column, so nothing written here reaches platform state. The platform never reads the bag — it is not an IAM context, not a policy input and not part of a prompt — which is what separates it from a tag bag.","required":false,"type":"object","in":"body"}]},"get-file":{serviceClass:"Files",operationId:"getFile",description:"Returns the data and metadata of a specific file",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"get",pathParams:["file_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"file_id","description":"File ID","required":true,"type":"string","in":"path"}]},"delete-file":{serviceClass:"Files",operationId:"deleteFile",description:"Removes a file from the system by ID",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"delete",pathParams:["file_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"file_id","description":"ID of the file to delete","required":true,"type":"string","in":"path"}]},"download-file":{serviceClass:"Files",operationId:"downloadFile",description:"Streams the file content to the client",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"get",pathParams:["file_id"],queryParams:["token"],headerParams:[],cookieParams:[],flags:[{"name":"file_id","description":"File ID","required":true,"type":"string","in":"path"},{"name":"token","description":"Signed single-file download token, an alternative to a bearer credential (issued for ingestion-rule converters).","required":false,"type":"string","in":"query"}]},"update-file-metadata":{serviceClass:"Files",operationId:"updateFileMetadata",description:"Updates the metadata field of a file",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"patch",pathParams:["file_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"file_id","description":"File ID","required":true,"type":"string","in":"path"},{"name":"metadata","description":"Replaces the stored bag; `null` clears it.","required":false,"type":"object","in":"body"},{"name":"prefix","description":"New directory — moves the file. The resulting path (prefix + filename) must be unique within the project.","required":false,"type":"string","in":"body"},{"name":"filename","description":"New filename — renames the key's leaf and the download name.","required":false,"type":"string","in":"body"}]},"download-file-base64":{serviceClass:"Files",operationId:"downloadFileBase64",description:"Returns the file content encoded as base64",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"get",pathParams:["file_id"],queryParams:["token"],headerParams:[],cookieParams:[],flags:[{"name":"file_id","description":"File ID","required":true,"type":"string","in":"path"},{"name":"token","description":"Signed single-file download token, an alternative to a bearer credential (issued for ingestion-rule converters).","required":false,"type":"string","in":"query"}]},"get-file-tags":{serviceClass:"Files",operationId:"getFileTags",description:"Returns all tags attached to the file",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"get",pathParams:["file_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"file_id","description":"File ID","required":true,"type":"string","in":"path"}]},"replace-file-tags":{serviceClass:"Files",operationId:"replaceFileTags",description:"Replaces all tags on the file with the provided tags",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"put",pathParams:["file_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"file_id","description":"File ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"merge-file-tags":{serviceClass:"Files",operationId:"mergeFileTags",description:"Merges provided tags into the file's existing tags (existing tags are preserved unless overridden)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/files",httpMethod:"patch",pathParams:["file_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"file_id","description":"File ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"validate-formation":{serviceClass:"Formations",operationId:"validateFormation",description:"Validates a formation template without creating any resources. Returns a list of errors and warnings. Accepts the template as a JSON object or as a YAML/JSON string.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/formations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"template","description":"A formation template supplied as either a JSON object or a YAML/JSON string. When a string is provided the server parses it with a YAML parser (JSON is valid YAML) before processing.\n","required":false,"type":"unknown","in":"body"},{"name":"parameters","description":"Runtime parameter values that override or supply template parameter defaults. Keys must match parameter names declared in `template.parameters`. When provided, the validation result also reports required parameters that are still missing after applying these values.\n","required":false,"type":"object","in":"body"}]},"plan-formation":{serviceClass:"Formations",operationId:"planFormation",description:"Computes a diff between the desired template and the current stack state without making any changes. Returns the list of planned actions.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/formations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID. Optional when authenticating with a project-scoped API key, which defaults to the key's project; required otherwise.","required":false,"type":"string","in":"body"},{"name":"formation_id","description":"Existing formation ID to compare against. Omit for new formation planning.","required":false,"type":"string","in":"body"},{"name":"template","description":"A formation template supplied as either a JSON object or a YAML/JSON string. When a string is provided the server parses it with a YAML parser (JSON is valid YAML) before processing.\n","required":true,"type":"unknown","in":"body"},{"name":"parameters","description":"Runtime parameter values that override or supply template parameter defaults. Keys must match parameter names declared in `template.parameters`. A parameter declared with `use_previous_value: true` may be omitted to reuse its stored value.\n","required":false,"type":"object","in":"body"}]},"list-formations":{serviceClass:"Formations",operationId:"listFormations",description:"Returns all formation stacks for a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/formations",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-formation":{serviceClass:"Formations",operationId:"createFormation",description:"Validates the template, creates the formation record, then provisions all declared resources in dependency order. A **template-shape** error is refused with `400`. A **deploy** failure is not: the operation ran, so the formation is returned with `201` and `status: \"failed\"`, and `error` explains why (the resources created before the failure are rolled back). Read `status` — a `2xx` here means the deploy was attempted, not that it worked. The `builtin` CLI exits non-zero on that body so `create-formation && …` does not lie.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/formations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID. Optional when authenticating with a project-scoped API key, which defaults to the key's project; required otherwise.","required":false,"type":"string","in":"body"},{"name":"name","description":"Human-readable name for the formation stack","required":true,"type":"string","in":"body"},{"name":"template","description":"A formation template supplied as either a JSON object or a YAML/JSON string. When a string is provided the server parses it with a YAML parser (JSON is valid YAML) before processing.\n","required":true,"type":"unknown","in":"body"},{"name":"parameters","description":"Runtime parameter values that override or supply template parameter defaults. Keys must match parameter names declared in `template.parameters`. Required parameters (those without a default) must be provided here.\n","required":false,"type":"object","in":"body"},{"name":"metadata","description":"Static annotations stored on the formation record. This field is NOT a substitution site: `sub`/`param`/`ref` expressions are rejected with 400 (`FORMATION_INVALID_METADATA`). For deploy-time substitution use the template's top-level `metadata` block, which is resolved into `resolved_metadata`.\n","required":false,"type":"object","in":"body"}]},"get-formation":{serviceClass:"Formations",operationId:"getFormation",description:"Returns the formation stack including its current resources.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/formations",httpMethod:"get",pathParams:["formation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"formation_id","description":"","required":true,"type":"string","in":"path"}]},"update-formation":{serviceClass:"Formations",operationId:"updateFormation",description:"Applies a new template to the formation. Resources are created, updated, or deleted to reconcile the current state with the desired state. A **template-shape** error is refused with `400`. A **deploy** failure is not: the operation ran, so the formation is returned with `200` and `status: \"failed\"`, and `error` explains why. Read `status` — a `2xx` here means the deploy was attempted, not that it worked. The `builtin` CLI exits non-zero on that body so `update-formation && …` does not lie. A deploy that replaced a resource and could not delete the superseded one answers `status: \"active\"` with `error.code: \"FORMATION_REPLACE_CLEANUP_FAILED\"` — the desired state is realised, and `error.meta.failures` names every resource still live. The next deploy retries the disposal.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/formations",httpMethod:"put",pathParams:["formation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"formation_id","description":"","required":true,"type":"string","in":"path"},{"name":"template","description":"A formation template supplied as either a JSON object or a YAML/JSON string. When a string is provided the server parses it with a YAML parser (JSON is valid YAML) before processing.\n","required":false,"type":"unknown","in":"body"},{"name":"parameters","description":"Runtime parameter values that override or supply template parameter defaults. Keys must match parameter names declared in `template.parameters`. Required parameters (those without a default) must be provided here, unless the parameter is declared with `use_previous_value: true`, in which case omitting it reuses the previously stored value.\n","required":false,"type":"object","in":"body"},{"name":"metadata","description":"Static annotations stored on the formation record. This field is NOT a substitution site: `sub`/`param`/`ref` expressions are rejected with 400 (`FORMATION_INVALID_METADATA`). For deploy-time substitution use the template's top-level `metadata` block, which is resolved into `resolved_metadata`.\n","required":false,"type":"object","in":"body"}]},"delete-formation":{serviceClass:"Formations",operationId:"deleteFormation",description:"Deletes the formation stack and all its managed resources in reverse dependency order. A resource the platform refuses to delete on its own — most often an agent that has generation or trace history — fails the teardown with `409 FORMATION_DELETE_FAILED`, naming every blocking resource in `error.meta.failures`. Resolve the blockers (for an agent, `DELETE /api/v1/agents/{agent_id}?force=true` also removes its generations and traces, and `deletion_policy: retain` exempts it from teardown entirely) and delete the formation again. A refusal the platform can foresee is found by a pre-flight, before the first delete: nothing is removed, and the formation stays `active` and intact for the retry. An unforeseeable error surfaces mid-teardown instead, where resources deleted before the blocker stay deleted and the formation is left in `delete_failed`. The error message states which happened.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/formations",httpMethod:"delete",pathParams:["formation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"formation_id","description":"","required":true,"type":"string","in":"path"}]},"list-formation-events":{serviceClass:"Formations",operationId:"listFormationEvents",description:"Returns all operations (create, update, delete) with their event logs for the formation, ordered chronologically.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/formations",httpMethod:"get",pathParams:["formation_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"formation_id","description":"","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"list-generations":{serviceClass:"Generations",operationId:"listGenerations",description:"Returns generations the caller can access, optionally filtered by agent, trace, orchestration run, node, and status. The generations of one trace are the `trace_id` filter. Filtering by `orchestration_run_id` is the supported way to get from an orchestration run to the generations its agent nodes produced: a node execution record carries no generation id, so the pointer lives here, alongside the run's other attribution columns.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/generations",httpMethod:"get",pathParams:[],queryParams:["agent_id","trace_id","session_id","actor_id","initiator_generation_id","chain_id","orchestration_run_id","node_id","status","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Filter by agent public ID","required":false,"type":"string","in":"query"},{"name":"trace_id","description":"Filter by trace public ID","required":false,"type":"string","in":"query"},{"name":"session_id","description":"Return only the generations dispatched through one session — the turns behind that conversation's spend. A session that does not exist in scope yields an empty page.\n","required":false,"type":"string","in":"query"},{"name":"actor_id","description":"Return only the generations one end user started, across every session they appear in. An actor that does not exist in scope yields an empty page.\n","required":false,"type":"string","in":"query"},{"name":"initiator_generation_id","description":"Filter by the public ID of the parent generation. Returns every generation started by that generation: sub-agent invocations, approval continuations, client-tool re-handoffs and memory-rule handler turns. Top-level generations are not returned.\n","required":false,"type":"string","in":"query"},{"name":"chain_id","description":"Filter by the continuation chain the generation belongs to. This is how a chain is expanded into its members — the chain record carries only their count.\n","required":false,"type":"string","in":"query"},{"name":"orchestration_run_id","description":"Filter by the orchestration run that dispatched the generation. This is how a run is traced back to what its agent nodes did — a node execution record stores no generation id.\n","required":false,"type":"string","in":"query"},{"name":"node_id","description":"Filter by the orchestration node that dispatched the generation. Combine with `orchestration_run_id` to narrow to one node of one run; a retried node returns one generation per `node_attempt`.\n","required":false,"type":"string","in":"query"},{"name":"status","description":"Filter by lifecycle status","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-generation":{serviceClass:"Generations",operationId:"getGeneration",description:"Returns a single generation record by ID, including its status and the structured `error` payload when the generation failed (e.g. because the upstream AI provider returned an error).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/generations",httpMethod:"get",pathParams:["generation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"generation_id","description":"Public ID of the generation","required":true,"type":"string","in":"path"}]},"update-generation":{serviceClass:"Generations",operationId:"updateGeneration",description:"Attaches caller-supplied key/value metadata to a generation record for per-run audit attribution (e.g. the ticket or case an AI action belongs to). The provided keys are shallow-merged over the existing `metadata`, so repeated patches accumulate. The bag is caller-owned and no key is reserved: server-owned state (usage attribution, the served agent version, the route's record, the extraction summary, what knowledge retrieval served) lives in its own top-level fields and cannot be written from here.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/generations",httpMethod:"patch",pathParams:["generation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"generation_id","description":"Public ID of the generation","required":true,"type":"string","in":"path"},{"name":"metadata","description":"Caller-supplied key/value metadata to shallow-merge into the generation record's caller-owned `metadata` bag. No key is reserved: server-owned state lives in its own top-level fields and cannot be written from here.\n","required":true,"type":"object","in":"body"}]},"purge-generation-content":{serviceClass:"Generations",operationId:"purgeGenerationContent",description:"Clears the generation's content — `metadata`, `error`, `extraction`, and the internal recovery state of a paused run — and stamps `content_redacted_at`. The usage and audit skeleton is preserved: ids, timestamps, status, stop reason, and the attribution fields (`action_id`, `trigger_id`, `orchestration_run_id`, `node_id`, `node_attempt`, `agent_version`, `routing`) the billing ledger reads. A purged generation reads back as that skeleton, not a 404. This does **not** delete the parent trace's steps object, which holds this generation's content alongside its siblings'. To erase the run's content completely, purge the trace (`DELETE /api/v1/traces/{trace_id}/content`), which cascades here. Idempotent — purging an already-purged generation succeeds and leaves the original `content_redacted_at` in place.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/generations",httpMethod:"delete",pathParams:["generation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"generation_id","description":"Public ID of the generation","required":true,"type":"string","in":"path"}]},"list-generation-memory-assertions":{serviceClass:"Generations",operationId:"listGenerationMemoryAssertions",description:"Every memory write this turn made, oldest first: each memory rule's firings and the agent's own `write_memory` calls alike. The rows behind the generation's per-rule `extraction` counts. Reading them is reading the generation (`generations:GetGeneration`).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/generations",httpMethod:"get",pathParams:["generation_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"generation_id","description":"Public ID of the generation","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-generation-transcript":{serviceClass:"Generations",operationId:"getGenerationTranscript",description:"Returns one generation's turn read back as an ordered sequence of steps: what it was asked, each model step with its tool calls and results, and how it ended. The transcript is assembled at read time from the generation record and the trace's steps object; nothing is stored, so it cannot outlive the content it projects. Requires `traces:GetTrace` in addition to `generations:GetGeneration`, because the response merges content from both resources. A generation whose content is unavailable — never written under zero-retention, or cleared by a purge — returns `200` with the skeleton rather than an error: `input` and `output` are null, `steps` is empty, and the `content_redacted_*` fields say which happened. `content_redacted_by_principal_id` is `zero_retention` when the content was never stored, and the purging principal's ID when it was erased later. A generation that is still running returns the same shape with an empty `steps`; `status` disambiguates the two.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/generations",httpMethod:"get",pathParams:["generation_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"generation_id","description":"Public ID of the generation","required":true,"type":"string","in":"path"}]},"list-guardrails":{serviceClass:"Guardrails",operationId:"listGuardrails",description:"Returns all guardrails in the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID to filter by","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-guardrail":{serviceClass:"Guardrails",operationId:"createGuardrail",description:"Creates a new guardrail in the project, archiving its document as version 1. The `document` is validated on write: `class` must be a literal (A/B/C/D) or a JSON Logic expression, and every variable it (and `guard`) reference must resolve to the `args.*` / `context.*` / `runtime.*` namespaces — an out-of-catalog `runtime.*` key is rejected with 400.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project","required":false,"type":"string","in":"body"},{"name":"name","description":"Human-readable name","required":true,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"document","description":"The action-class document. `class` maps a call to an action class; `guard` gates class-B autonomy. Both are single JSON Logic expressions over the `args.*` / `context.*` / `runtime.*` namespaces.\n","required":true,"type":"object","in":"body"},{"name":"context_tool_id","description":"","required":false,"type":"string","in":"body"},{"name":"context_mode","description":"","required":false,"type":"string","in":"body"},{"name":"version_label","description":"Optional tag for the config version this write archives (e.g. `initial`). Annotates the version only — it is not stored on the guardrail and is not part of the config, so labelling a change is never itself a change.","required":false,"type":"string","in":"body"}]},"get-guardrail":{serviceClass:"Guardrails",operationId:"getGuardrail",description:"Returns a single guardrail by ID.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"get",pathParams:["guardrail_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"guardrail_id","description":"","required":true,"type":"string","in":"path"}]},"update-guardrail":{serviceClass:"Guardrails",operationId:"updateGuardrail",description:"Updates an existing guardrail. A `document` write that actually changes the policy increments `version` and archives the new document as a GuardrailVersion; metadata-only edits (name / description / context), and re-writing the document the guardrail already holds, leave the version untouched.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"patch",pathParams:["guardrail_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"guardrail_id","description":"","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"document","description":"The action-class document. `class` maps a call to an action class; `guard` gates class-B autonomy. Both are single JSON Logic expressions over the `args.*` / `context.*` / `runtime.*` namespaces.\n","required":false,"type":"object","in":"body"},{"name":"context_tool_id","description":"","required":false,"type":"string","in":"body"},{"name":"context_mode","description":"","required":false,"type":"string","in":"body"},{"name":"version_label","description":"Optional tag for the config version this write archives (e.g. `pre-tightening`). Annotates the version only — it is not stored on the guardrail and is not part of the config, so labelling a change is never itself a change. Ignored when the write changes no policy, since no version is created.","required":false,"type":"string","in":"body"},{"name":"expected_version","description":"Refuses the write unless the resource is at this version.","required":false,"type":"integer","in":"body"}]},"delete-guardrail":{serviceClass:"Guardrails",operationId:"deleteGuardrail",description:"Deletes a guardrail and its archived versions by ID.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"delete",pathParams:["guardrail_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"guardrail_id","description":"","required":true,"type":"string","in":"path"}]},"list-guardrail-versions":{serviceClass:"Guardrails",operationId:"listGuardrailVersions",description:"Returns the guardrail's archived configurations, newest first. A version is written on create and on every subsequent write that changes the policy `document` — through the REST API or a formation apply alike. Metadata-only edits (name, description, context binding) do not archive a version. See [Versioning](/docs/modules/guardrails#versioning).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"get",pathParams:["guardrail_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"guardrail_id","description":"","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-guardrail-version":{serviceClass:"Guardrails",operationId:"getGuardrailVersion",description:"Returns the exact configuration — and so the exact `document` — that governed at a given version. Approval items, activity entries, and exceptions record the version that governed them, so the audit chain survives edits.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"get",pathParams:["guardrail_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"guardrail_id","description":"","required":true,"type":"string","in":"path"},{"name":"version","description":"","required":true,"type":"integer","in":"path"}]},"restore-guardrail-version":{serviceClass:"Guardrails",operationId:"restoreGuardrailVersion",description:"Writes an archived version's `document` back as the guardrail's live policy, which archives it again as a **new** version rather than rewinding the counter — so an approval item or exception citing any version in between still resolves. The restore runs through the ordinary update path, so the archived document is re-validated; restoring the policy the guardrail already holds is a no-op and archives nothing.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"post",pathParams:["guardrail_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"guardrail_id","description":"","required":true,"type":"string","in":"path"},{"name":"version","description":"","required":true,"type":"integer","in":"path"},{"name":"label","description":"Optional tag for the version the restore creates. Defaults to `restored from v<version>`.","required":false,"type":"string","in":"body"}]},"evaluate-guardrail":{serviceClass:"Guardrails",operationId:"evaluateGuardrail",description:"Runs the full evaluation pipeline — the `class` expression, the guard, the context tool per `context_mode`, live `runtime.*` resolution — against caller-supplied `args` and `guardrail_context`, and returns the exact `guardrail_evaluation` record a real call would produce. Nothing executes, no approval item is filed, and no activity entry is written. This is the adoption path: preview a document's decisions against production-shaped calls before attaching it — or before editing a widely-attached one. Pass an optional `tool_id` to resolve `runtime.tools.*`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/guardrails",httpMethod:"post",pathParams:["guardrail_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"guardrail_id","description":"","required":true,"type":"string","in":"path"},{"name":"args","description":"The proposed call's arguments (the `args.*` namespace).","required":false,"type":"object","in":"body"},{"name":"guardrail_context","description":"The caller-supplied guardrail context (the `context.*` namespace), combined with the context tool per `context_mode`.\n","required":false,"type":"object","in":"body"},{"name":"tool_id","description":"Optional tool to resolve `runtime.tools.*` against.","required":false,"type":"string","in":"body"}]},"list-ingestion-rules":{serviceClass:"IngestionRules",operationId:"listIngestionRules",description:"Returns the ingestion rules for a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ingestion-rules",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-ingestion-rule":{serviceClass:"IngestionRules",operationId:"createIngestionRule",description:"Creates a rule mapping a content_type glob to a converter. Exactly one of tool_id or agent_id must be set.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ingestion-rules",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"content_type_glob","description":"MIME type glob matched against a file's content_type. At most 4 wildcards and 255 characters — a MIME glob needs one on each side of the slash at most.","required":true,"type":"string","in":"body"},{"name":"tool_id","description":"Converter tool id (mutually exclusive with agent_id)","required":false,"type":"string","in":"body"},{"name":"agent_id","description":"Converter agent id (mutually exclusive with tool_id)","required":false,"type":"string","in":"body"},{"name":"action","description":"Operation id, required for builtin/mcp tool converters","required":false,"type":"string","in":"body"},{"name":"preset_parameters","description":"Merged into the tool input before invocation (tool converters only)","required":false,"type":"object","in":"body"},{"name":"native_extraction","description":"For native types (PDF/text): `first` (default) converts only when native extraction yields no text; `skip` always converts.","required":false,"type":"string","in":"body"},{"name":"file_delivery","description":"How the file reaches a tool converter (default base64)","required":false,"type":"string","in":"body"},{"name":"chunk_strategy","description":"Default chunk strategy, overridable per ingest request","required":false,"type":"string","in":"body"},{"name":"chunk_size","description":"Default window size in characters for the size strategy","required":false,"type":"integer","in":"body"},{"name":"chunk_overlap","description":"Default overlap in characters for the size strategy","required":false,"type":"integer","in":"body"},{"name":"metadata","description":"Arbitrary JSON metadata","required":false,"type":"object","in":"body"}]},"get-ingestion-rule":{serviceClass:"IngestionRules",operationId:"getIngestionRule",description:"Returns a specific ingestion rule",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ingestion-rules",httpMethod:"get",pathParams:["ingestion_rule_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ingestion_rule_id","description":"Ingestion rule ID","required":true,"type":"string","in":"path"}]},"update-ingestion-rule":{serviceClass:"IngestionRules",operationId:"updateIngestionRule",description:"Updates fields of an ingestion rule",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ingestion-rules",httpMethod:"patch",pathParams:["ingestion_rule_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ingestion_rule_id","description":"Ingestion rule ID","required":true,"type":"string","in":"path"},{"name":"content_type_glob","description":"MIME type glob matched against a file's content_type. At most 4 wildcards and 255 characters.","required":false,"type":"string","in":"body"},{"name":"tool_id","description":"","required":false,"type":"string","in":"body"},{"name":"agent_id","description":"","required":false,"type":"string","in":"body"},{"name":"action","description":"","required":false,"type":"string","in":"body"},{"name":"preset_parameters","description":"","required":false,"type":"object","in":"body"},{"name":"native_extraction","description":"","required":false,"type":"string","in":"body"},{"name":"file_delivery","description":"","required":false,"type":"string","in":"body"},{"name":"chunk_strategy","description":"Send `null` to clear the rule's override and fall back to the per-request default.","required":false,"type":"string","in":"body"},{"name":"chunk_size","description":"","required":false,"type":"integer","in":"body"},{"name":"chunk_overlap","description":"","required":false,"type":"integer","in":"body"},{"name":"metadata","description":"A `MetadataBag` on a field where `null` is meaningful — a full-replacement update that clears the bag, or a record whose bag was never set.","required":false,"type":"object","in":"body"}]},"delete-ingestion-rule":{serviceClass:"IngestionRules",operationId:"deleteIngestionRule",description:"Deletes an ingestion rule",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/ingestion-rules",httpMethod:"delete",pathParams:["ingestion_rule_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"ingestion_rule_id","description":"Ingestion rule ID","required":true,"type":"string","in":"path"}]},"search-knowledge":{serviceClass:"Knowledge",operationId:"searchKnowledge",description:"Searches across documents and memories using hybrid retrieval — a vector query and a full-text query per source, fused by reciprocal rank — or by file paths, document IDs, memory store IDs, or tags. At least one of `query`, `tags`, `document_paths`, `document_ids`, or `memory_store_ids` must be provided.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/knowledge",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Limit search to a specific project","required":false,"type":"string","in":"body"},{"name":"query","description":"Search query text. Runs a vector query and a full-text query over each store and fuses the rankings; the full-text channel matches exact tokens and phrases, the vector channel everything else.","required":false,"type":"string","in":"body"},{"name":"min_similarity","description":"Minimum raw cosine `similarity_score` a **vector** candidate must reach to take part in ranking, applied before fusion. Only applies when `query` is provided. Lexical candidates are deliberately exempt: a result that literally contains the searched token is the evidence, and dropping it for a low cosine is the failure hybrid search exists to prevent. Not a floor on `score`, whose fused value encodes rank position rather than similarity.","required":false,"type":"number","in":"body"},{"name":"rrf_k","description":"The `k` in the reciprocal rank fusion term `1 / (k + rank)`, which sets how steeply a result's contribution decays with its position in each ranked list. A smaller value weights the very top of each list more heavily. Defaults to the deployment's `KNOWLEDGE_RRF_K`, itself 60 by default. Only applies when `query` is provided.","required":false,"type":"integer","in":"body"},{"name":"recency_half_life_days","description":"Half-life, in days, of a recency decay applied to **memory store** results after fusion: a result's `score` is multiplied by `2 ^ (-age_in_days / recency_half_life_days)`, where age is measured from its `updated_at`. Document results are never decayed. `0` — the default, and the default of the deployment's `KNOWLEDGE_RECENCY_HALF_LIFE_DAYS` — disables the blend entirely; it is a switch, not a lower bound, and sending `0` turns off a deployment-wide decay for this one request. Accepts fractions (`0.5` is twelve hours). How many ranks a given half-life costs depends on `rrf_k`. Only applies when `query` is provided. **No value is known to be safe in general**: every half-life measured against the reference corpus that lifted recency-sensitive queries also cost relevance-sensitive ones, which is why this ships disabled. Measure against your own corpus before setting it — see the Retrieval Quality guide.","required":false,"type":"number","in":"body"},{"name":"limit","description":"Maximum number of results to return (default 10). A value above 100 is clamped to 100 — the ceiling bounds the vector scan this one request performs, so a larger `limit` returns everything there is up to that many rows rather than being refused.","required":false,"type":"integer","in":"body"},{"name":"include_documents","description":"Set `false` to leave the document store out of this search. A `query` names no store, so it reaches both; the store-specific filters narrow *within* a store rather than choosing between them. This is the switch, and a filter naming the other store never overrides it. `false` for both stores is `400`.","required":false,"type":"boolean","in":"body"},{"name":"include_memories","description":"Set `false` to leave the memory store out of this search. The mirror of `include_documents`, for a caller that wants documents alone.","required":false,"type":"boolean","in":"body"},{"name":"memory_store_ids","description":"Search memories within these specific memory stores","required":false,"type":"array","in":"body"},{"name":"document_paths","description":"Filter results to documents whose file path starts with one of these prefixes","required":false,"type":"array","in":"body"},{"name":"document_ids","description":"Filter results to specific document IDs","required":false,"type":"array","in":"body"},{"name":"tags","description":"Filter results to documents and memories whose `tags` contain every one of these key-value pairs (exact, case-sensitive match). Scopes both stores, so passing it alone searches both — as `query` does. For memories it matches at memory granularity: a memory is returned when its parent memory store's tags match or its own do.","required":false,"type":"object","in":"body"},{"name":"metadata","description":"Filter document results by their `metadata` bag. A document-store filter: a memory carries no such bag, so passing it alone searches documents, as `document_paths` does.","required":false,"type":"object","in":"body"}]},"list-memories":{serviceClass:"Memories",operationId:"listMemories",description:"Returns all memories in a memory store",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"get",pathParams:[],queryParams:["memory_store_id","tags","limit","offset","include_invalidated"],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"Memory store to list memories from (mstore_...)","required":true,"type":"string","in":"query"},{"name":"tags","description":"Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n","required":false,"type":"array","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"include_invalidated","description":"Include invalidated memories — superseded or retracted. They are excluded by default; set this to audit what a store once held.","required":false,"type":"boolean","in":"query"}]},"create-memory":{serviceClass:"Memories",operationId:"createMemory",description:"Writes a fact to the specified memory store through the standard write algorithm: the content is embedded (or matched to text the store already holds), compared against the most similar currently-valid memory, and resolved to exactly one of three outcomes — `skipped` at or above `duplicate_threshold`, `superseded` at or above `supersede_threshold`, `created` below it. `supersedes` overrides that comparison entirely, retiring the memory it names. Every call records one assertion, whatever the outcome.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"Memory store to add the memory to (mstore_...)","required":true,"type":"string","in":"body"},{"name":"content","description":"The text content of the memory","required":true,"type":"string","in":"body"},{"name":"source_type","description":"Whether there is a source to point at. `conversation` requires `source_id`; `manual` rejects it.","required":false,"type":"string","in":"body"},{"name":"source_id","description":"The conversation this fact was learned in. Required when `source_type` is `conversation`, and rejected otherwise.","required":false,"type":"string","in":"body"},{"name":"tags","description":"Per-memory key-value tags, used for memory-granularity filtering by `tags` in search-knowledge.","required":false,"type":"object","in":"body"},{"name":"metadata","description":"Arbitrary structured metadata attached to the memory","required":false,"type":"object","in":"body"},{"name":"supersedes","description":"The memory this write replaces, named outright. The declaration outranks the thresholds in both directions: the target is retired and the write returns `superseded` however similar or distant the two texts are. This is what reaches a contradiction cosine cannot see (\"The office is in Lisbon\" then \"We closed the Lisbon office\"). The target must be a still-valid memory in the same memory store, and the caller needs `memories:UpdateMemory` on it as well as `memories:CreateMemory` on the store.","required":false,"type":"string","in":"body"},{"name":"duplicate_threshold","description":"Cosine similarity at or above which the incoming content is a duplicate of an existing memory and the write is skipped. Overrides the store's `duplicate_threshold` for this call; falls back to the store's value, then to `0.95`.","required":false,"type":"number","in":"body"},{"name":"supersede_threshold","description":"Cosine similarity at or above which the incoming content restates a fact that has changed: the top match is invalidated and a new memory replaces it. Overrides the store's `supersede_threshold` for this call; falls back to the store's value, then to `0.90`. The effective pair must satisfy `supersede_threshold < duplicate_threshold`, and the check runs against the effective pair — so overriding only one value cannot invert it against the store's other one.","required":false,"type":"number","in":"body"}]},"get-memory":{serviceClass:"Memories",operationId:"getMemory",description:"Returns a single memory by ID",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"get",pathParams:["memory_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_id","description":"","required":true,"type":"string","in":"path"}]},"update-memory":{serviceClass:"Memories",operationId:"updateMemory",description:"Updates an existing memory. Regenerates the embedding if content changes.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"put",pathParams:["memory_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"memory_id","description":"","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"content","description":"Updated text content","required":false,"type":"string","in":"body"},{"name":"tags","description":"Replaces the memory's tags. Pass null or an empty object to clear.","required":false,"type":"object","in":"body"},{"name":"metadata","description":"Replaces the memory's metadata. Pass null to clear.","required":false,"type":"object","in":"body"},{"name":"expected_version","description":"Refuses the write unless the memory is at this version.","required":false,"type":"integer","in":"body"}]},"delete-memory":{serviceClass:"Memories",operationId:"deleteMemory",description:"Deletes a memory",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"delete",pathParams:["memory_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_id","description":"","required":true,"type":"string","in":"path"}]},"retract-memory":{serviceClass:"Memories",operationId:"retractMemory",description:"Retires a fact that stopped holding with nothing replacing it. The memory leaves the default listing, [knowledge search](/docs/api/knowledge/search-knowledge) and write deduplication, so restating the fact later lands as a new memory. It is an invalidation with no successor, which is what tells it apart from a supersede: `invalidated_at` is set and `superseded_by_memory_id` stays null. The retraction is appended to the assertion ledger with outcome `retracted`, so who withdrew the fact is part of the record. The memory stays readable by id, with its text and its assertions. `DELETE` remains the way to remove it outright.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"post",pathParams:["memory_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"memory_id","description":"","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"expected_version","description":"Refuses the retraction unless the memory is at this version.","required":false,"type":"integer","in":"body"}]},"list-memory-assertions":{serviceClass:"Memories",operationId:"listMemoryAssertions",description:"Returns every write that resolved into this memory, oldest first: the one that created it, the duplicates it absorbed, and the assertion that superseded another memory in its favour. A superseded memory keeps its own assertions, so the chain can be walked in both directions.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"get",pathParams:["memory_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"memory_id","description":"","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-memory-tags":{serviceClass:"Memories",operationId:"getMemoryTags",description:"Returns all tags attached to the memory",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"get",pathParams:["memory_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_id","description":"Memory ID","required":true,"type":"string","in":"path"}]},"replace-memory-tags":{serviceClass:"Memories",operationId:"replaceMemoryTags",description:"Replaces all tags on the memory with the provided tags (not merged)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"put",pathParams:["memory_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_id","description":"Memory ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"merge-memory-tags":{serviceClass:"Memories",operationId:"mergeMemoryTags",description:"Merges provided tags into the memory's existing tags (existing tags are preserved unless overridden)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memories",httpMethod:"patch",pathParams:["memory_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_id","description":"Memory ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"list-memory-rules":{serviceClass:"MemoryRules",operationId:"listMemoryRules",description:"Returns memory rules, newest first. Narrow to one store with `memory_store_id` — that form is authorized against the store itself, so it answers \"what feeds this store?\" in one call.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-rules",httpMethod:"get",pathParams:[],queryParams:["memory_store_id","project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"Only the rules of this memory store","required":false,"type":"string","in":"query"},{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-memory-rule":{serviceClass:"MemoryRules",operationId:"createMemoryRule",description:"Creates an ingestion rule on a memory store. With neither `agent_id` nor `tool_id` the built-in extractor runs, configurable through `prompt`, `ai_provider_id` and `model`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-rules",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"The destination store, and the rule's owning scope","required":true,"type":"string","in":"body"},{"name":"on","description":"The event a firing reads. `agents.generation.completed` fires once per completed turn whatever the transport, and is the only event the built-in extractor may bind to. `conversations.message.generated` fires per persisted assistant reply and is conversation-backed, so it is for custom handlers.","required":true,"type":"string","in":"body"},{"name":"source_agent_ids","description":"Agents whose turns this rule reads. Omit or send `null` for every agent in the store's project.","required":false,"type":"array","in":"body"},{"name":"agent_id","description":"Handler agent (mutually exclusive with tool_id)","required":false,"type":"string","in":"body"},{"name":"tool_id","description":"Handler tool (mutually exclusive with agent_id)","required":false,"type":"string","in":"body"},{"name":"action","description":"Operation id, for a tool handler","required":false,"type":"string","in":"body"},{"name":"preset_parameters","description":"Merged into a tool handler's input before invocation. The turn's own fields are reserved and win.","required":false,"type":"object","in":"body"},{"name":"prompt","description":"Replaces the built-in extractor's task instructions. The JSON response contract and the transcript are always appended. Not valid with a handler.","required":false,"type":"string","in":"body"},{"name":"ai_provider_id","description":"Provider override for the built-in extractor's completion. Not valid with a handler.","required":false,"type":"string","in":"body"},{"name":"model","description":"Model override for the built-in extractor's completion. Not valid with a handler.","required":false,"type":"string","in":"body"},{"name":"enabled","description":"A disabled rule is kept and never fires","required":false,"type":"boolean","in":"body"}]},"get-memory-rule":{serviceClass:"MemoryRules",operationId:"getMemoryRule",description:"Returns a specific memory rule",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-rules",httpMethod:"get",pathParams:["memory_rule_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_rule_id","description":"Memory rule ID","required":true,"type":"string","in":"path"}]},"update-memory-rule":{serviceClass:"MemoryRules",operationId:"updateMemoryRule",description:"Updates a memory rule. Validation runs against the rule as it would stand after the change, so a one-field update cannot pair a handler with a stored extractor override from the side.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-rules",httpMethod:"patch",pathParams:["memory_rule_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_rule_id","description":"Memory rule ID","required":true,"type":"string","in":"path"},{"name":"on","description":"The event a firing reads. `agents.generation.completed` fires once per completed turn whatever the transport, and is the only event the built-in extractor may bind to. `conversations.message.generated` fires per persisted assistant reply and is conversation-backed, so it is for custom handlers.","required":false,"type":"string","in":"body"},{"name":"source_agent_ids","description":"Send `null` to widen the rule to every agent in the project","required":false,"type":"array","in":"body"},{"name":"agent_id","description":"","required":false,"type":"string","in":"body"},{"name":"tool_id","description":"","required":false,"type":"string","in":"body"},{"name":"action","description":"","required":false,"type":"string","in":"body"},{"name":"preset_parameters","description":"","required":false,"type":"object","in":"body"},{"name":"prompt","description":"","required":false,"type":"string","in":"body"},{"name":"ai_provider_id","description":"","required":false,"type":"string","in":"body"},{"name":"model","description":"","required":false,"type":"string","in":"body"},{"name":"enabled","description":"","required":false,"type":"boolean","in":"body"}]},"delete-memory-rule":{serviceClass:"MemoryRules",operationId:"deleteMemoryRule",description:"Deletes a memory rule. The assertions it wrote are kept; their `rule_id` becomes null.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-rules",httpMethod:"delete",pathParams:["memory_rule_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_rule_id","description":"Memory rule ID","required":true,"type":"string","in":"path"}]},"list-memory-stores":{serviceClass:"MemoryStores",operationId:"listMemoryStores",description:"Returns a list of memory store configurations for a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"get",pathParams:[],queryParams:["project_id","tags","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"tags","description":"Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n","required":false,"type":"array","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-memory-store":{serviceClass:"MemoryStores",operationId:"createMemoryStore",description:"Creates a new memory store configuration in a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"name","description":"Memory store name","required":true,"type":"string","in":"body"},{"name":"description","description":"Optional description","required":false,"type":"string","in":"body"},{"name":"tags","description":"Optional key-value tags. Scopes the memory store in knowledge search, where every requested pair must match exactly.","required":false,"type":"object","in":"body"},{"name":"duplicate_threshold","description":"The store's dedup policy: cosine similarity at or above which an incoming fact is already known and the write is skipped. `null` (the default) uses the algorithm constant `0.95`. A single `POST /api/v1/memories` call may override it; the agent tool and the post-turn rule never can.","required":false,"type":"number","in":"body"},{"name":"supersede_threshold","description":"Cosine similarity at or above which an incoming fact restates a known one that has changed: the matched memory is invalidated and replaced. `null` (the default) uses the algorithm constant `0.90`. Must be lower than the effective `duplicate_threshold`, or the write is rejected with `400 VALIDATION_FAILED` — equal makes `superseded` unreachable and inverted swallows `skipped`.","required":false,"type":"number","in":"body"}]},"get-memory-store":{serviceClass:"MemoryStores",operationId:"getMemoryStore",description:"Returns a single memory store configuration by ID",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"get",pathParams:["memory_store_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"","required":true,"type":"string","in":"path"}]},"update-memory-store":{serviceClass:"MemoryStores",operationId:"updateMemoryStore",description:"Updates an existing memory store configuration",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"put",pathParams:["memory_store_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"","required":true,"type":"string","in":"path"},{"name":"name","description":"Memory store name","required":false,"type":"string","in":"body"},{"name":"description","description":"Optional description","required":false,"type":"string","in":"body"},{"name":"tags","description":"Optional key-value tags. Replaces the stored bag; `null` clears it.","required":false,"type":"object","in":"body"},{"name":"duplicate_threshold","description":"The store's dedup policy: cosine similarity at or above which an incoming fact is already known and the write is skipped. `null` (the default) uses the algorithm constant `0.95`. A single `POST /api/v1/memories` call may override it; the agent tool and the post-turn rule never can.","required":false,"type":"number","in":"body"},{"name":"supersede_threshold","description":"Cosine similarity at or above which an incoming fact restates a known one that has changed: the matched memory is invalidated and replaced. `null` (the default) uses the algorithm constant `0.90`. Must be lower than the effective `duplicate_threshold`, or the write is rejected with `400 VALIDATION_FAILED` — equal makes `superseded` unreachable and inverted swallows `skipped`.","required":false,"type":"number","in":"body"}]},"delete-memory-store":{serviceClass:"MemoryStores",operationId:"deleteMemoryStore",description:"Deletes a memory store configuration",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"delete",pathParams:["memory_store_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"","required":true,"type":"string","in":"path"}]},"list-memory-store-assertions":{serviceClass:"MemoryStores",operationId:"listMemoryStoreAssertions",description:"Returns the store's write ledger, newest first: one row per write attempt, skips included. This is how \"which write path is filling this store?\" is asked — a question no column on a memory row could answer, because a skipped write produced no memory at all.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"get",pathParams:["memory_store_id"],queryParams:["mechanism","outcome","generation_id","since","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"","required":true,"type":"string","in":"path"},{"name":"mechanism","description":"Only assertions that came through this door.","required":false,"type":"string","in":"query"},{"name":"outcome","description":"Only assertions that resolved this way.","required":false,"type":"string","in":"query"},{"name":"generation_id","description":"Only assertions made during this generation. A generation that does not exist matches nothing rather than widening to the whole store.","required":false,"type":"string","in":"query"},{"name":"since","description":"Only assertions recorded at or after this instant.","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"export-memories":{serviceClass:"MemoryStores",operationId:"exportMemories",description:"Streams one store's memories as newline-delimited JSON — one memory object per line, oldest first. Invalidated memories (retracted or superseded) are left out unless `include_invalidated` asks for them, so the file holds what the store currently asserts. The rows are the rows the listing returns for the same caller.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"get",pathParams:["memory_store_id"],queryParams:["include_invalidated","tags"],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"Memory store whose memories are exported","required":true,"type":"string","in":"path"},{"name":"include_invalidated","description":"Include memories that were retracted or superseded","required":false,"type":"boolean","in":"query"},{"name":"tags","description":"Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n","required":false,"type":"array","in":"query"}]},"get-memory-store-tags":{serviceClass:"MemoryStores",operationId:"getMemoryStoreTags",description:"Returns all tags attached to the memory store",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"get",pathParams:["memory_store_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"Memory store ID","required":true,"type":"string","in":"path"}]},"replace-memory-store-tags":{serviceClass:"MemoryStores",operationId:"replaceMemoryStoreTags",description:"Replaces all tags on the memory store with the provided tags (not merged)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"put",pathParams:["memory_store_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"Memory store ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"merge-memory-store-tags":{serviceClass:"MemoryStores",operationId:"mergeMemoryStoreTags",description:"Merges provided tags into the memory store's existing tags (existing tags are preserved unless overridden)",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/memory-stores",httpMethod:"patch",pathParams:["memory_store_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"memory_store_id","description":"Memory store ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"list-metadata-schemas":{serviceClass:"MetadataSchemas",operationId:"listMetadataSchemas",description:"Returns the declarations in scope, oldest first. `resource_type` narrows them to one governed resource.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/metadata-schemas",httpMethod:"get",pathParams:[],queryParams:["project_id","resource_type","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID (proj_ prefix)","required":false,"type":"string","in":"query"},{"name":"resource_type","description":"Return only the declarations governing this resource.","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-metadata-schema":{serviceClass:"MetadataSchemas",operationId:"createMetadataSchema",description:"Declares what `metadata` must satisfy for one resource type under one selector. A document is selected by `path_prefix`, matched on a path boundary: `/reports` covers `/reports/q1.txt` and never `/reports-archive/q1.txt`. The schema is compiled here, so one JSON Schema cannot parse is refused rather than stored — a stored one would be a rule that silently governs nothing. One selector has one schema per resource type; a second declaration of the same one is `409 NAME_CONFLICT`. The reserved root `/.system` cannot be governed: a platform-written document carries no caller metadata.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/metadata-schemas",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project the declaration governs. Optional for a project-scoped API key, which supplies it.","required":false,"type":"string","in":"body"},{"name":"resource_type","description":"The resource whose metadata this declaration governs. A type appears here once its write path reads the registry, so a declaration always has a door that enforces it.","required":true,"type":"string","in":"body"},{"name":"path_prefix","description":"The selector a `document` declaration must carry: the directory it governs.","required":false,"type":"string","in":"body"},{"name":"schema","description":"A JSON Schema. Its keywords are its own vocabulary and are stored as written.","required":true,"type":"object","in":"body"}]},"validate-metadata":{serviceClass:"MetadataSchemas",operationId:"validateMetadata",description:"Answers what a write would be told, without writing: a caller preparing a batch learns which declaration would refuse it, and why, before it sends anything. It reports; it does not enforce. The refusal itself lives in each resource's own write path, because a check a writer has to call is advisory and the writer who skips it is the one the rule exists for.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/metadata-schemas",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"","required":false,"type":"string","in":"body"},{"name":"path","description":"The path the document would be filed at, which decides which declaration governs it.","required":true,"type":"string","in":"body"},{"name":"metadata","description":"The bag to judge. Absent or `null` is judged as an empty bag.","required":false,"type":"object","in":"body"}]},"get-metadata-schema":{serviceClass:"MetadataSchemas",operationId:"getMetadataSchema",description:"Returns one declaration by id.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/metadata-schemas",httpMethod:"get",pathParams:["metadata_schema_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"metadata_schema_id","description":"","required":true,"type":"string","in":"path"}]},"update-metadata-schema":{serviceClass:"MetadataSchemas",operationId:"updateMetadataSchema",description:"Changes the schema, the selector, or both. `resource_type` is fixed at creation: it decides the selector's spelling and which write path reads the row, so changing it would silently repoint the declaration at a different door — delete it and declare again instead.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/metadata-schemas",httpMethod:"patch",pathParams:["metadata_schema_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"metadata_schema_id","description":"","required":true,"type":"string","in":"path"},{"name":"path_prefix","description":"The declaration's new selector.","required":false,"type":"string","in":"body"},{"name":"schema","description":"Replaces the declared JSON Schema.","required":false,"type":"object","in":"body"}]},"delete-metadata-schema":{serviceClass:"MetadataSchemas",operationId:"deleteMetadataSchema",description:"Removes the declaration. Documents already stored keep the metadata they hold — the rule governed writes, not rows.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/metadata-schemas",httpMethod:"delete",pathParams:["metadata_schema_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"metadata_schema_id","description":"","required":true,"type":"string","in":"path"}]},"list-model-routes":{serviceClass:"ModelRoutes",operationId:"listModelRoutes",description:"Returns the model routes defined in a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/model-routes",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-model-route":{serviceClass:"ModelRoutes",operationId:"createModelRoute",description:"Creates a project-scoped model route: a named, ordered list of provider+model targets tried in array order. Every target must reference an AI provider in the same project (400 otherwise), and the total attempt budget — the sum of `1 + max_retries` over all targets — may not exceed 10 (400 naming the computed total). A duplicate `name` in the project is rejected with 409.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/model-routes",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"name","description":"Human-readable name, unique per project","required":true,"type":"string","in":"body"},{"name":"targets","description":"Ordered failover targets. Position is priority: target 0 is tried first, and a retryable failure falls through to the next target.","required":true,"type":"array","in":"body"},{"name":"retry_on","description":"Which failure classes are failover-eligible. A failure whose class is not listed (and every deterministic failure — 400-class, auth, content policy) fails the generation immediately instead of spending another target's budget.","required":false,"type":"array","in":"body"},{"name":"failure_threshold","description":"Consecutive retryable failures after which a target is skipped for `cooldown_seconds`. Breaker state is in-process per node and keyed by (provider, model), so it is shared by every route pointing at the same backend.","required":false,"type":"integer","in":"body"},{"name":"cooldown_seconds","description":"How long a tripped target is skipped before being probed again","required":false,"type":"integer","in":"body"}]},"get-model-route":{serviceClass:"ModelRoutes",operationId:"getModelRoute",description:"Returns a specific model route",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/model-routes",httpMethod:"get",pathParams:["route_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"route_id","description":"Model route ID","required":true,"type":"string","in":"path"}]},"update-model-route":{serviceClass:"ModelRoutes",operationId:"updateModelRoute",description:"Updates a model route's name, targets, retry classes, or breaker configuration. Omitted fields are left unchanged.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/model-routes",httpMethod:"put",pathParams:["route_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"route_id","description":"Model route ID","required":true,"type":"string","in":"path"},{"name":"name","description":"New name (unique per project)","required":false,"type":"string","in":"body"},{"name":"targets","description":"Replacement target list (ordered)","required":false,"type":"array","in":"body"},{"name":"retry_on","description":"","required":false,"type":"array","in":"body"},{"name":"failure_threshold","description":"","required":false,"type":"integer","in":"body"},{"name":"cooldown_seconds","description":"","required":false,"type":"integer","in":"body"}]},"delete-model-route":{serviceClass:"ModelRoutes",operationId:"deleteModelRoute",description:"Deletes a model route. Returns 409 when an agent still references it — a routed agent has no pinned provider to fall back on, so the reference must be repointed or the agent deleted first.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/model-routes",httpMethod:"delete",pathParams:["route_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"route_id","description":"Model route ID","required":true,"type":"string","in":"path"}]},"list-orchestrations":{serviceClass:"Orchestrations",operationId:"listOrchestrations",description:"Returns orchestrations accessible to the caller.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Filter by project public ID","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-orchestration":{serviceClass:"Orchestrations",operationId:"createOrchestration",description:"Creates a new orchestration (pipeline) definition in the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project. Optional when authenticating with a project-scoped API key, which defaults to the key's project; required otherwise.","required":false,"type":"string","in":"body"},{"name":"name","description":"Human-readable name.","required":true,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"nodes","description":"The graph's nodes. Each `id` is unique and is what edges and `nodes.<id>` state references name; a node with no incoming edge starts the run.","required":true,"type":"array","in":"body"},{"name":"edges","description":"Directed edges between node `id`s. Every `from`/`to` must name a node in `nodes`, and the graph must be acyclic unless it contains a `loop` node. A node with several outgoing edges activates every target in parallel. `[]` for a graph of independent nodes.","required":true,"type":"array","in":"body"},{"name":"state_schema","description":"","required":false,"type":"object","in":"body"},{"name":"input_schema","description":"","required":false,"type":"object","in":"body"},{"name":"output_mapping","description":"The shape of a succeeded run's `output`, owned by the orchestration rather than by its terminal node ids. Each key is an output field (a dotted key such as `summary.title` builds a nested object); each value is JSON Logic (https://jsonlogic.com) evaluated against `{ \"state\": <final run state> }`, which includes `state.input` and `state.nodes.<id>`. A missing path maps to `null`; a mapping that throws fails the run. Null (or omitted) keys `output` by terminal node id. Versioned with the graph; `null` on update clears it.","required":false,"type":"object","in":"body"},{"name":"version_label","description":"Optional tag for the version this create archives, e.g. `initial`.","required":false,"type":"string","in":"body"}]},"validate-orchestration":{serviceClass:"Orchestrations",operationId:"validateOrchestration",description:"Statically validates an orchestration graph without persisting anything. Checks that every node has its required field, node ids are unique, edges reference existing nodes, the graph is acyclic (unless it contains a loop node), and every `input_mapping` `{\"var\": \"...\"}` reference resolves to a state key written by an upstream node or seeded by `input_schema`. Returns blocking `errors` and non-blocking `warnings` (e.g. a state key only written on a conditional branch). The same `errors` checks are enforced on create and update, which fail with `400` when any error is present.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"nodes","description":"The graph's nodes. Each `id` is unique and is what edges and `nodes.<id>` state references name; a node with no incoming edge starts the run. Checked exactly as create checks them, without persisting.","required":false,"type":"array","in":"body"},{"name":"edges","description":"Directed edges between node `id`s. Every `from`/`to` must name a node in `nodes`, and the graph must be acyclic unless it contains a `loop` node. A node with several outgoing edges activates every target in parallel. Checked exactly as create checks them, without persisting.","required":false,"type":"array","in":"body"},{"name":"input_schema","description":"Optional JSON Schema for run inputs; its top-level properties seed state.","required":false,"type":"object","in":"body"}]},"get-queue-stats":{serviceClass:"Orchestrations",operationId:"getQueueStats",description:"Returns a point-in-time snapshot of the orchestration run queue: how many tasks are waiting to be claimed (`queue_depth`), how many are currently claimed with a valid lease (`claimed_tasks`), the age of the oldest waiting task, recent claim-latency percentiles over a rolling in-process window. The per-project breakdown is listed by `GET /api/v1/orchestrations/queue/stats/projects`. Intended for admin/operator policies; guarded by `orchestrations:GetQueueStats`. Every figure is scoped to what the caller may see. A project-scoped caller gets `queue_depth` and `claimed_tasks` summed over their own projects, and `null` for `oldest_queued_age_seconds` and the `claim_latency_ms` percentiles — both describe the whole deployment and cannot be narrowed, so they are withheld rather than approximated. An unrestricted caller (the action granted on every project) gets the deployment-wide figures.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[]},"list-queue-stats-projects":{serviceClass:"Orchestrations",operationId:"listQueueStatsProjects",description:"One row per project with any queued or claimed task, ordered by project ID: the per-project breakdown of `GET /api/v1/orchestrations/queue/stats`. A project-scoped caller sees its own projects only. Guarded by `orchestrations:GetQueueStats`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:[],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-orchestration":{serviceClass:"Orchestrations",operationId:"getOrchestration",description:"Returns the orchestration with nodes and edges.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:["orchestration_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_id","description":"Public ID of the orchestration (orch_...)","required":true,"type":"string","in":"path"}]},"update-orchestration":{serviceClass:"Orchestrations",operationId:"updateOrchestration",description:"Partially updates an orchestration definition.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"patch",pathParams:["orchestration_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"orchestration_id","description":"Public ID of the orchestration (orch_...)","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"nodes","description":"The graph's nodes. Each `id` is unique and is what edges and `nodes.<id>` state references name; a node with no incoming edge starts the run. Replaces the stored list; omitted, the stored nodes are kept and validated against the new `edges`.","required":false,"type":"array","in":"body"},{"name":"edges","description":"Directed edges between node `id`s. Every `from`/`to` must name a node in `nodes`, and the graph must be acyclic unless it contains a `loop` node. A node with several outgoing edges activates every target in parallel. Replaces the stored list; omitted, the stored edges are kept and validated against the new `nodes`.","required":false,"type":"array","in":"body"},{"name":"state_schema","description":"","required":false,"type":"object","in":"body"},{"name":"input_schema","description":"","required":false,"type":"object","in":"body"},{"name":"output_mapping","description":"The shape of a succeeded run's `output`, owned by the orchestration rather than by its terminal node ids. Each key is an output field (a dotted key such as `summary.title` builds a nested object); each value is JSON Logic (https://jsonlogic.com) evaluated against `{ \"state\": <final run state> }`, which includes `state.input` and `state.nodes.<id>`. A missing path maps to `null`; a mapping that throws fails the run. Null (or omitted) keys `output` by terminal node id. Versioned with the graph; `null` on update clears it.","required":false,"type":"object","in":"body"},{"name":"version_label","description":"Optional tag for the version this write archives, e.g. `pre-rewire`. Ignored when the write changes no graph field, since no version is archived.","required":false,"type":"string","in":"body"},{"name":"expected_version","description":"Refuses the write unless the resource is at this version.","required":false,"type":"integer","in":"body"}]},"delete-orchestration":{serviceClass:"Orchestrations",operationId:"deleteOrchestration",description:"Deletes an orchestration definition and all its runs.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"delete",pathParams:["orchestration_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_id","description":"Public ID of the orchestration (orch_...)","required":true,"type":"string","in":"path"}]},"list-orchestration-versions":{serviceClass:"Orchestrations",operationId:"listOrchestrationVersions",description:"Returns the orchestration's archived graphs, newest first. A version is written on create and on every subsequent write that changes the graph (`nodes`, `edges`, `state_schema`, `input_schema`) — through the REST API or a formation apply alike. Metadata-only edits (name, description) do not archive a version. See [Versioning](/docs/modules/orchestrations#versioning).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:["orchestration_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_id","description":"Public ID of the orchestration (orch_...)","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-orchestration-version":{serviceClass:"Orchestrations",operationId:"getOrchestrationVersion",description:"Returns the exact graph a given version describes. Every run records the version it started on in `orchestration_version` and executes that graph for its whole life, so this is how you read the topology a run actually took — including a run whose orchestration has been rewired since.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:["orchestration_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_id","description":"Public ID of the orchestration (orch_...)","required":true,"type":"string","in":"path"},{"name":"version","description":"The archived version number","required":true,"type":"integer","in":"path"}]},"restore-orchestration-version":{serviceClass:"Orchestrations",operationId:"restoreOrchestrationVersion",description:"Writes an archived version's graph back as the orchestration's live definition, which archives it again as a **new** version rather than rewinding the counter — so a run pinned to any version in between still resolves the graph it started on. The restore runs through the ordinary update path, so the archived graph goes through the same static validation as an authored one. Node resource references (`agent_id`, `tool_id`, `orchestration_id`) resolve when a run reaches the node, so a target deleted since the snapshot was taken restores cleanly and surfaces as a failed run rather than a `400`. Restoring the graph the orchestration already holds is a no-op and archives nothing. Runs already in flight are unaffected either way — a restore is an ordinary edit, and pinning is what keeps it from reaching them.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"post",pathParams:["orchestration_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_id","description":"Public ID of the orchestration (orch_...)","required":true,"type":"string","in":"path"},{"name":"version","description":"The archived version number","required":true,"type":"integer","in":"path"},{"name":"label","description":"Optional tag for the version the restore creates. Defaults to `restored from v<version>`.","required":false,"type":"string","in":"body"}]},"list-orchestration-runs":{serviceClass:"Orchestrations",operationId:"listOrchestrationRuns",description:"Returns orchestration runs the caller can access, optionally filtered by orchestration, by parent run, by status, or by whether the run has a parent at all. Note when aggregating: a run's `usage` covers its whole subtree, so summing it over a list that contains both a parent and its children counts the children more than once. Pass `nested=false` to sum over runs a caller started.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:[],queryParams:["project_id","orchestration_id","parent_orchestration_run_id","nested","status","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Narrow the listing to one project (proj_...). A project-scoped key lists its own project without it.","required":false,"type":"string","in":"query"},{"name":"orchestration_id","description":"Filter by orchestration public ID (orch_...)","required":false,"type":"string","in":"query"},{"name":"parent_orchestration_run_id","description":"Filter to the runs one specific parent run's `loop` / `sub_orchestration` nodes started (run_...). This is how a caller holding a parent names the individual children behind its `usage`.","required":false,"type":"string","in":"query"},{"name":"nested","description":"Filter by whether the run was started by another run. `false` returns only the runs a caller started (no parent), which is the set to sum `usage` over; `true` returns only the runs a `loop` / `sub_orchestration` node started, across every parent. Omit to return both.\n\nContradicting `parent_orchestration_run_id` with `nested=false` is a `400`; any value other than `true` or `false` is a `400`.","required":false,"type":"boolean","in":"query"},{"name":"status","description":"Filter by run status. Repeat the parameter to OR values — `status=queued&status=running&status=sleeping&status=awaiting_input` is the set still driving, which is how a caller finds live work without paging every run the project ever started.\n\nThere is no `non_terminal` shorthand on purpose: which statuses count as live is the caller's policy. A value outside the enum, empty string included, is a `400`.","required":false,"type":"array","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"start-orchestration-run":{serviceClass:"Orchestrations",operationId:"startOrchestrationRun",description:"Creates a new run for the orchestration named by orchestration_id. By default the run executes durably in the background: the response returns immediately with status \"queued\" (a worker then claims it and moves it to \"running\") and progress is observed via get-orchestration-run or run lifecycle webhook events (orchestration_runs.started/awaiting_input/succeeded/failed). Delay and poll waits park the run as \"sleeping\" and are woken by a background scheduler, surviving restarts. Pass wait=true to block until the run reaches a terminal or awaiting_input state.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"post",pathParams:[],queryParams:[],headerParams:["Idempotency-Key"],cookieParams:[],flags:[{"name":"Idempotency-Key","description":"The request's deduplication key. Equivalent to `idempotency_key` in the request body; sending both with different values is `400 VALIDATION_FAILED`. A non-empty string of at most 255 characters.","required":false,"type":"string","in":"header"},{"name":"orchestration_id","description":"Orchestration to run (orch_...).","required":true,"type":"string","in":"body"},{"name":"input","description":"Initial state for the run (merged with orchestration defaults).","required":false,"type":"object","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as `X-Soat-Context-<key>` headers on every `http`, `mcp` and `builtin` tool call made by an agent node of this run — including the agents of any child run a `loop` or `sub_orchestration` node starts. The header name is `X-Soat-Context-` plus the key verbatim; no character is re-cased.\n\nThe bag is stored on the run and re-read on every step, so it survives an `awaiting_input` pause, a `sleeping` wait, a background worker drive and a crash redrive. It is **write-only**: a run is a record every principal who may read runs can read, and a credential in it is not theirs to see, so it is never returned on one. It also does not outlive the run: reaching a terminal status (`succeeded`, `failed`, `cancelled` or `expired`) clears it. A key that is not a valid HTTP header name, or two keys that map to the same header, are rejected with `400 INVALID_TOOL_CONTEXT_KEY` and no run is created.\n\nThe reserved identity keys (`session_id`, `actor_id`, `actor_external_id`) are stripped at generation time — a caller cannot address them from here.","required":false,"type":"object","in":"body"},{"name":"metadata","description":"Caller-supplied key/value metadata attached to the run record for per-run attribution (e.g. which of your own tenants this run belongs to, or the dispatch batch that started it). Round-trips verbatim on every read of the run, on the list as well as the single read.\n\nThe bag is caller-owned and no key is reserved: server-owned state (status, the pinned orchestration version, the trace, usage, artifacts, the run's own `input` and accumulated `state`) lives in its own top-level field and cannot be written from here.\n\nIt is **not** merged into run state: no graph node sees it, and an `input_schema` never has to tolerate it — which is what makes it the place for an infrastructural label, rather than `input`. Keys are never transformed. It is not inherited by the child runs a `loop` or `sub_orchestration` node starts; each child carries whatever the graph gives it, which today is nothing.","required":false,"type":"object","in":"body"},{"name":"idempotency_key","description":"Deduplication key, unique within the project, that makes a retry of an ambiguous failure safe. The first request under a key starts the run and answers `201`; any later request carrying the same key answers `200` with that same run, whatever state it has reached — including a retry that arrives while the original is still running.\n\nThe key is claimed by the run record and stays claimed for as long as the record exists, so it never silently expires and lets a second run through.\n\n`orchestration_id`, `input`, `tool_context` and `metadata` are the request the key names: reusing a key with any of them changed is `409 IDEMPOTENCY_KEY_REUSED` rather than a replay of a run that does something else. `wait` is not part of that comparison — it says how the caller waits, not what the run is — so a retry may flip it.","required":false,"type":"string","in":"body"},{"name":"wait","description":"When true, block until the run reaches a terminal (succeeded/failed) or awaiting_input state and return the settled run. When false (default), return immediately with status \"queued\" and execute the run in the background.","required":false,"type":"boolean","in":"body"}]},"cancel-orchestration-run":{serviceClass:"Orchestrations",operationId:"cancelOrchestrationRun",description:"Cancels a run that has not yet reached a terminal state.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"post",pathParams:["orchestration_run_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_run_id","description":"Public ID of the run (run_...)","required":true,"type":"string","in":"path"}]},"pause-orchestration-run":{serviceClass:"Orchestrations",operationId:"pauseOrchestrationRun",description:"Parks a run in flight as `awaiting_input` at its next checkpoint, with a `required_action` of type `paused` naming the pause as operator-initiated rather than a node's. Unlike cancel, the run keeps its last checkpoint and resume-orchestration-run re-drives it from there — so work already done is deferred rather than discarded. A `queued` or `sleeping` run is parked immediately (a sleeping run keeps the wake it was due, and resuming hands it back to the scheduler at that instant). A `running` run keeps running until the round in flight reaches its checkpoint, so the response may still read `running` while `pause_requested_at` is set. A run already parked on a human, webhook or approval node keeps that node's `required_action`; the pause is still recorded, which is what makes submit-human-input refuse until the run is resumed. The pause fans out to the run's `loop` / `sub_orchestration` descendants — each parks at its own next checkpoint — because otherwise a parent's pause would bound nothing. Resuming does not fan out: each parked descendant is resumed by its own id. Idempotent: pausing an already-paused run answers with it unchanged.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"post",pathParams:["orchestration_run_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_run_id","description":"Public ID of the run (run_...)","required":true,"type":"string","in":"path"},{"name":"reason","description":"Why the run is being paused, surfaced on the parked run's `required_action.reason` and on `pause_reason`.","required":false,"type":"string","in":"body"}]},"submit-human-input":{serviceClass:"Orchestrations",operationId:"submitHumanInput",description:"Provides human input to a run that is awaiting_input at a human node.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"post",pathParams:["orchestration_run_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_run_id","description":"Public ID of the run (run_...)","required":true,"type":"string","in":"path"},{"name":"node_id","description":"ID of the human node to satisfy.","required":true,"type":"string","in":"body"},{"name":"output","description":"Output/response provided by the human reviewer.","required":false,"type":"object","in":"body"}]},"resume-orchestration-run":{serviceClass:"Orchestrations",operationId:"resumeOrchestrationRun",description:"Re-drives an awaiting_input orchestration run from its last checkpoint. This does not satisfy the pause itself — it carries no node_id or payload, so a run parked on a human or webhook-receive node re-parks on the same node. Use submit-human-input to supply the awaited payload and advance the run. It is also the only thing that lifts an operator pause (pause-orchestration-run): a run parked with `required_action.type` of `paused` re-drives the frontier that had not run yet, and one paused mid-timer goes back to `sleeping` for the wake it was already due.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"post",pathParams:["orchestration_run_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_run_id","description":"Public ID of the run (run_...)","required":true,"type":"string","in":"path"}]},"get-orchestration-run":{serviceClass:"Orchestrations",operationId:"getOrchestrationRun",description:"Returns the status, state, and artifacts of a specific run.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:["orchestration_run_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_run_id","description":"Public ID of the run (run_...)","required":true,"type":"string","in":"path"}]},"list-orchestration-run-node-executions":{serviceClass:"Orchestrations",operationId:"listOrchestrationRunNodeExecutions",description:"The run's per-node execution records, oldest first: one per node attempt, retries and skipped nodes included. Each captures the resolved input, output, status and error of that attempt — the orchestration analogue of an LLM trace. Reading them is reading the run (`orchestrations:GetRun`).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/orchestrations",httpMethod:"get",pathParams:["orchestration_run_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"orchestration_run_id","description":"Public ID of the run (run_...)","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"list-playbooks":{serviceClass:"Playbooks",operationId:"listPlaybooks",description:"Returns the playbooks defined in a project, newest first.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-playbook":{serviceClass:"Playbooks",operationId:"createPlaybook",description:"Creates a playbook at version 1. The definition is refused with `400 PLAYBOOK_VALIDATION_FAILED` when it holds a duplicate node or input, an input reading a missing node or field, an `execute` node without an executor or another kind with one, transformations on a non-final node, or a cycle; `meta.problems` lists every one. A duplicate `name` in the project is `409 NAME_CONFLICT`. The caller becomes the version's author, whom its executors run as, so it must be allowed to start every executor the definition names (`403 PLAYBOOK_EXECUTOR_FORBIDDEN`). A definition with executors written with a trigger or OAuth token is `409 PLAYBOOK_AUTHOR_UNAVAILABLE`: those carry their boundary in the token, so no run can act as them.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"name","description":"Human-readable name, unique per project","required":true,"type":"string","in":"body"},{"name":"description","description":"What the playbook is for","required":false,"type":"string","in":"body"},{"name":"definition","description":"The nodes and how their inputs are sourced","required":true,"type":"object","in":"body"},{"name":"version_label","description":"Optional tag for version 1, e.g. `initial`","required":false,"type":"string","in":"body"}]},"validate-playbook":{serviceClass:"Playbooks",operationId:"validatePlaybook",description:"Answers what a create or update would be told about a definition, without writing: `valid`, and every graph problem it holds. A definition whose shape cannot be read is `400 VALIDATION_FAILED`, as on a write.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"definition","description":"The definition to check","required":true,"type":"object","in":"body"}]},"get-playbook":{serviceClass:"Playbooks",operationId:"getPlaybook",description:"Returns a playbook with its current definition and version.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"get",pathParams:["playbook_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"playbook_id","description":"The playbook ID","required":true,"type":"string","in":"path"}]},"update-playbook":{serviceClass:"Playbooks",operationId:"updatePlaybook",description:"Updates any of `name`, `description` and `definition`. Only a change to `definition` archives a new version; a rename or a rewrite of the definition the playbook already holds leaves `version` where it is. A case keeps the version it was created on. A new version is authored by the caller, checked as on create: `403 PLAYBOOK_EXECUTOR_FORBIDDEN` for an executor it may not start, `409 PLAYBOOK_AUTHOR_UNAVAILABLE` for a trigger or OAuth token.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"patch",pathParams:["playbook_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"playbook_id","description":"The playbook ID","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"name","description":"Human-readable name, unique per project","required":false,"type":"string","in":"body"},{"name":"description","description":"What the playbook is for","required":false,"type":"string","in":"body"},{"name":"definition","description":"Replaces the whole definition; a change archives a new version","required":false,"type":"object","in":"body"},{"name":"version_label","description":"Optional tag for the version this write archives. Ignored when the write changes no definition, since no version is archived.","required":false,"type":"string","in":"body"},{"name":"expected_version","description":"Refuses the write unless the playbook is at this version.","required":false,"type":"integer","in":"body"}]},"delete-playbook":{serviceClass:"Playbooks",operationId:"deletePlaybook",description:"Deletes a playbook and its version archive. A playbook with cases is refused with `409 PLAYBOOK_HAS_CASES`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"delete",pathParams:["playbook_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"playbook_id","description":"The playbook ID","required":true,"type":"string","in":"path"}]},"list-playbook-versions":{serviceClass:"Playbooks",operationId:"listPlaybookVersions",description:"Returns the playbook's archived definitions, newest first. A version is written on create and on every write that changes `definition`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"get",pathParams:["playbook_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"playbook_id","description":"The playbook ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-playbook-version":{serviceClass:"Playbooks",operationId:"getPlaybookVersion",description:"Returns the definition a version held: what a case pinned to it is evaluated against.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"get",pathParams:["playbook_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"playbook_id","description":"The playbook ID","required":true,"type":"string","in":"path"},{"name":"version","description":"The archived version number","required":true,"type":"integer","in":"path"}]},"restore-playbook-version":{serviceClass:"Playbooks",operationId:"restorePlaybookVersion",description:"Writes an archived definition back as the playbook's live one, which archives it again as a new version rather than rewinding the counter. Restoring the definition the playbook already holds is a no-op. The caller authors the new version, checked as on create.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"post",pathParams:["playbook_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"playbook_id","description":"The playbook ID","required":true,"type":"string","in":"path"},{"name":"version","description":"The archived version number","required":true,"type":"integer","in":"path"},{"name":"label","description":"Optional tag for the new version. Defaults to `restored from vN`.","required":false,"type":"string","in":"body"}]},"create-playbook-case":{serviceClass:"Playbooks",operationId:"createPlaybookCase",description:"Creates a case for one subject on the playbook's current version. The case stays on that version however the playbook changes later.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"post",pathParams:["playbook_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"playbook_id","description":"The playbook ID","required":true,"type":"string","in":"path"},{"name":"subject","description":"Who or what the case is about, as you name it","required":true,"type":"string","in":"body"}]},"list-playbook-cases":{serviceClass:"Playbooks",operationId:"listPlaybookCases",description:"Returns a project's cases, newest first, without node states; read one case for those.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"get",pathParams:[],queryParams:["project_id","playbook_id","automation_status","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"playbook_id","description":"Only the cases of this playbook","required":false,"type":"string","in":"query"},{"name":"automation_status","description":"Only the cases in this automation status","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-playbook-case":{serviceClass:"Playbooks",operationId:"getPlaybookCase",description:"Returns a case with every node's state, computed from its entries on each read and never stored.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"get",pathParams:["case_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"case_id","description":"The case ID","required":true,"type":"string","in":"path"}]},"delete-playbook-case":{serviceClass:"Playbooks",operationId:"deletePlaybookCase",description:"Deletes a case and every entry written to it.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"delete",pathParams:["case_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"case_id","description":"The case ID","required":true,"type":"string","in":"path"}]},"write-playbook-case-inputs":{serviceClass:"Playbooks",operationId:"writePlaybookCaseInputs",description:"Writes a batch of manual inputs, all or none. Each write names the revision it read in `expected_revision`, `null` for a field still unset; when any field has moved on the whole batch is refused with `409 PLAYBOOK_INPUT_CONFLICT` and `meta.conflicts` names each field. An input the case's version does not declare, one that reads another node, or a value its schema refuses is `400 PLAYBOOK_INPUT_INVALID`. A value equal to the field's current one writes no revision. Once written, every node the write makes runnable is queued to run, unless the case is paused. A write made with a credential the case's own work runs as — the case, one of its executions in flight, or the task or run one started — is `403 PLAYBOOK_SELF_WRITE`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"post",pathParams:["case_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"case_id","description":"The case ID","required":true,"type":"string","in":"path"},{"name":"inputs","description":"The writes, applied all or none","required":true,"type":"array","in":"body"}]},"get-playbook-case-missing":{serviceClass:"Playbooks",operationId:"getPlaybookCaseMissing",description:"Returns the manual inputs still unset, or refused by their schema, before `node` can be reached, grouped in waves: nothing in a later wave can run before the earlier ones are filled.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"get",pathParams:["case_id"],queryParams:["node"],headerParams:[],cookieParams:[],flags:[{"name":"case_id","description":"The case ID","required":true,"type":"string","in":"path"},{"name":"node","description":"The key of the node to reach, usually a final node","required":true,"type":"string","in":"query"}]},"list-playbook-case-entries":{serviceClass:"Playbooks",operationId:"listPlaybookCaseEntries",description:"Returns every value written to the case, newest first. Entries are never rewritten, so this is the case's full history.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"get",pathParams:["case_id"],queryParams:["node","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"case_id","description":"The case ID","required":true,"type":"string","in":"path"},{"name":"node","description":"Only the entries of this node","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"run-playbook-case-node":{serviceClass:"Playbooks",operationId:"runPlaybookCaseNode",description:"Runs one node now. The cascade already runs every node whose inputs are set once a write, a settle or a resume makes it runnable, and never reruns a `failed` one: this is how a failed node is retried. A `collect` node takes its own input values as its output, any other node starts its executor with the node's input values mapped through the executor's `input`. The executor runs as the author of the case's playbook version, whose permission to start it is checked again here (`403 PLAYBOOK_EXECUTOR_FORBIDDEN`); an author that no longer exists is `409 PLAYBOOK_AUTHOR_UNAVAILABLE`. Only a `ready`, `stale` or `failed` node with every required input set runs; anything else is `409 PLAYBOOK_NODE_NOT_RUNNABLE`. A paused case is `409 PLAYBOOK_CASE_PAUSED`. The output is written as an entry when the execution settles. A value equal to the node's current output writes no revision. If the node's inputs change while it runs, the execution settles as `superseded` and writes nothing. Without `wait` the execution answers `running` and settles in the background. With `wait: true` it answers once the executor settles, or once it parks: a workflow task still open, or an orchestration run waiting on a person or a timer, settles the node when it closes or ends.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"post",pathParams:["case_id","node"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"case_id","description":"The case ID","required":true,"type":"string","in":"path"},{"name":"node","description":"The key of a node of the case's playbook version","required":true,"type":"string","in":"path"},{"name":"wait","description":"Answer once the executor settles or parks instead of right away","required":false,"type":"boolean","in":"body"}]},"list-playbook-case-executions":{serviceClass:"Playbooks",operationId:"listPlaybookCaseExecutions",description:"Returns every run of the case's nodes, newest first.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"get",pathParams:["case_id"],queryParams:["node","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"case_id","description":"The case ID","required":true,"type":"string","in":"path"},{"name":"node","description":"Only the executions of this node","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-playbook-case-execution":{serviceClass:"Playbooks",operationId:"getPlaybookCaseExecution",description:"Returns one run of a case's node.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"get",pathParams:["case_id","execution_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"case_id","description":"The case ID","required":true,"type":"string","in":"path"},{"name":"execution_id","description":"The execution ID","required":true,"type":"string","in":"path"}]},"export-playbook-case-node":{serviceClass:"Playbooks",operationId:"exportPlaybookCaseNode",description:"Renders one revision of a final node as files, one export per transformation: each transformation's tool is called with the case's `case_id` and `subject`, the `node`, the `revision` and that revision's `output`, and must answer a file (`execute.response_mode: base64`) of its `format`'s content type. The file is kept in the project's files under `/.system/playbooks/`. An export never feeds a node. Without `revision` the node's current output is exported, which needs the node `done` (`409 PLAYBOOK_NODE_NOT_EXPORTABLE`); a `revision` names an earlier output or confirmation of the node. Each tool runs as the author of the case's playbook version, whose permission to call it is checked here (`403 PLAYBOOK_EXECUTOR_FORBIDDEN`). A paused case is `409 PLAYBOOK_CASE_PAUSED`. Without `wait` every export answers `running` and settles in the background; with `wait: true` it answers once each has its file or its error.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"post",pathParams:["case_id","node"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"case_id","description":"The case ID","required":true,"type":"string","in":"path"},{"name":"node","description":"The key of a node of the case's playbook version","required":true,"type":"string","in":"path"},{"name":"transformations","description":"The names of the transformations to render; every one the node declares when omitted","required":false,"type":"array","in":"body"},{"name":"revision","description":"The output or confirmation of the node to render; its current output when omitted","required":false,"type":"integer","in":"body"},{"name":"wait","description":"Answer once every export settles instead of right away. A tool call, `builtin` or MCP, always waits.","required":false,"type":"boolean","in":"body"}]},"list-playbook-case-exports":{serviceClass:"Playbooks",operationId:"listPlaybookCaseExports",description:"Returns every file rendered from the case's final nodes, newest first.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"get",pathParams:["case_id"],queryParams:["node","transformation","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"case_id","description":"The case ID","required":true,"type":"string","in":"path"},{"name":"node","description":"Only the exports of this node","required":false,"type":"string","in":"query"},{"name":"transformation","description":"Only the exports of this transformation","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-playbook-case-export":{serviceClass:"Playbooks",operationId:"getPlaybookCaseExport",description:"Returns one file rendered from a case's final node.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"get",pathParams:["case_id","export_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"case_id","description":"The case ID","required":true,"type":"string","in":"path"},{"name":"export_id","description":"The export ID","required":true,"type":"string","in":"path"}]},"upgrade-playbook-case":{serviceClass:"Playbooks",operationId:"upgradePlaybookCase",description:"Moves a case to another version of its playbook: the playbook's current version, or the `version` named, an earlier one included. Values carry over by node key, and every node's state is read against the new version: a node added is open, a node removed leaves the structure while its entries stay, a node whose input reads another field is `stale`, and a value its input's new schema refuses is missing again. The cascade then runs what became runnable. Each upgrade is recorded with what moved; the version the case is already on records nothing. An execution running is `409 PLAYBOOK_CASE_BUSY`; one queued but not started settles `superseded`. An upgrade made with a credential the case's own work runs as is `403 PLAYBOOK_SELF_WRITE`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"post",pathParams:["case_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"case_id","description":"The case ID","required":true,"type":"string","in":"path"},{"name":"version","description":"The version to move the case to; the playbook's current one when omitted","required":false,"type":"integer","in":"body"}]},"list-playbook-case-upgrades":{serviceClass:"Playbooks",operationId:"listPlaybookCaseUpgrades",description:"Returns every version change of the case, newest first.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"get",pathParams:["case_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"case_id","description":"The case ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"pause-playbook-case":{serviceClass:"Playbooks",operationId:"pausePlaybookCase",description:"Stops the case starting anything: no node is queued and no queued execution starts until it is resumed. Executions already running settle. Input writes are still accepted. Pausing a paused case keeps the first pause.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"post",pathParams:["case_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"case_id","description":"The case ID","required":true,"type":"string","in":"path"}]},"resume-playbook-case":{serviceClass:"Playbooks",operationId:"resumePlaybookCase",description:"Lifts the pause and starts what became runnable while the case was paused. A case that is not paused is `409 PLAYBOOK_CASE_NOT_PAUSED`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"post",pathParams:["case_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"case_id","description":"The case ID","required":true,"type":"string","in":"path"}]},"confirm-playbook-case-node":{serviceClass:"Playbooks",operationId:"confirmPlaybookCaseNode",description:"Approves or rejects a `confirm` node on the inputs the confirmer read. `expected_revisions` names the revision of each of the node's inputs as read (`nodes[].inputs[].revision` on the case); an input that moved since is `409 PLAYBOOK_INPUT_CONFLICT`, `meta.conflicts` naming each, and nothing is written. An approval writes a `confirmation` entry whose value is the node's input values: it is the node's output, and the nodes reading it run next. A rejection writes a `rejection` entry with the `reason`: the node is `rejected`, and every node reading it is blocked, until its inputs change or it is approved. Another node, or a `confirm` node with a required input unset or reading a node that is not `done`, is `409 PLAYBOOK_NODE_NOT_CONFIRMABLE`. A confirmation made with a credential the case's own work runs as is `403 PLAYBOOK_SELF_WRITE`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/playbooks",httpMethod:"post",pathParams:["case_id","node"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"case_id","description":"The case ID","required":true,"type":"string","in":"path"},{"name":"node","description":"The key of a node of the case's playbook version","required":true,"type":"string","in":"path"},{"name":"decision","description":"Whether the node's inputs, as read, are approved","required":true,"type":"string","in":"body"},{"name":"reason","description":"Why the inputs were rejected; required on a rejection","required":false,"type":"string","in":"body"},{"name":"expected_revisions","description":"Each of the node's inputs, by name, mapped to the revision read, or null for one read as unset","required":true,"type":"object","in":"body"}]},"list-policies":{serviceClass:"Policies",operationId:"listPolicies",description:"Returns global policies. Requires admin role. Pass user_id to list only the policies attached to that user.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/policies",httpMethod:"get",pathParams:[],queryParams:["user_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"user_id","description":"Return only policies attached to this user (user_...)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-policy":{serviceClass:"Policies",operationId:"createPolicy",description:"Creates a new global policy. Requires admin role.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/policies",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"document","description":"","required":true,"type":"object","in":"body"}]},"get-policy":{serviceClass:"Policies",operationId:"getPolicy",description:"Returns details of a specific policy. Requires admin role.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/policies",httpMethod:"get",pathParams:["policy_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"policy_id","description":"Policy public ID (pol_ prefix)","required":true,"type":"string","in":"path"}]},"update-policy":{serviceClass:"Policies",operationId:"updatePolicy",description:"Updates an existing global policy. Requires admin role.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/policies",httpMethod:"put",pathParams:["policy_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"policy_id","description":"Policy public ID (pol_ prefix)","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"document","description":"","required":true,"type":"object","in":"body"}]},"delete-policy":{serviceClass:"Policies",operationId:"deletePolicy",description:"Deletes a global policy. Requires admin role.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/policies",httpMethod:"delete",pathParams:["policy_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"policy_id","description":"Policy public ID (pol_ prefix)","required":true,"type":"string","in":"path"}]},"list-projects":{serviceClass:"Projects",operationId:"listProjects",description:"Lists projects accessible to the caller. - JWT admin: returns all projects. - JWT regular user: returns only projects granted by the user's policies. - API key scoped to a project: returns only that project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"get",pathParams:[],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-project":{serviceClass:"Projects",operationId:"createProject",description:"Creates a new project. Requires admin role.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"name","description":"","required":true,"type":"string","in":"body"}]},"get-project":{serviceClass:"Projects",operationId:"getProject",description:"Returns details of a specific project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"get",pathParams:["project_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID (proj_ prefix)","required":true,"type":"string","in":"path"}]},"update-project":{serviceClass:"Projects",operationId:"updateProject",description:"Updates a project's name, its attached guardrails (`guardrail_ids` — the project-scope baseline governing every tool call by every agent in the project), its orchestration concurrency limit (`max_concurrent_runs`), its continuation-chain ceiling (`max_chain_generations`), its orchestration nesting bound (`max_orchestration_run_depth`), its inherited model route (`default_model_route_id`), its read-auditing opt-in (`audit_reads_enabled`), its priced-model requirement (`require_priced_model`), its trace-content retention window (`trace_content_retention_days`), and/or its zero-retention setting (`trace_content_mode`). At least one field is required. Requires admin role. Detaching a guardrail (removing an id) additionally requires guardrails:DetachGuardrail.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"patch",pathParams:["project_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID (proj_ prefix)","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"guardrail_ids","description":"Guardrails attached at the project scope.","required":false,"type":"array","in":"body"},{"name":"max_concurrent_runs","description":"Maximum orchestration runs of this project driven at once. `null` clears the limit (unlimited); otherwise an integer >= 1. Enforced at queue claim time — excess runs stay queued until a slot frees.","required":false,"type":"integer","in":"body"},{"name":"max_chain_generations","description":"Generations one continuation chain in this project may hold before the platform stops resuming it. `null` clears the project's ceiling, leaving the deployment-wide `MAX_CONTINUATION_CHAIN_GENERATIONS`; otherwise an integer >= 1. The effective budget is the smallest of the deployment's ceiling, this one, and the agent's own `max_chain_generations` stop condition, so an agent author can be stricter than this number but never exceed it.","required":false,"type":"integer","in":"body"},{"name":"max_orchestration_run_depth","description":"`loop` / `sub_orchestration` nesting levels a run tree in this project may reach before the engine refuses to start the next child. `null` clears the project's bound, leaving the deployment-wide `MAX_ORCHESTRATION_RUN_DEPTH` (default 10); otherwise an integer >= 1. The effective bound is the smaller of the two, so this can only be stricter than the deployment's — which is what makes it a bound a graph author cannot opt out of. A refused child fails its parent run with `ORCHESTRATION_RUN_DEPTH_LIMIT`.","required":false,"type":"integer","in":"body"},{"name":"default_model_route_id","description":"Model route inherited by consumers in this project that bind neither `model_route_id` nor `ai_provider_id`. The route must belong to this project (`400` otherwise). `null` clears the default, which is refused with `409` while any consumer inherits it — repointing it to another route is always allowed and immediately changes which targets those consumers use.","required":false,"type":"string","in":"body"},{"name":"audit_reads_enabled","description":"Opt the project into read auditing. When true, `GET` requests that name this project are recorded in the audit log alongside mutations. Defaults to `false`.","required":false,"type":"boolean","in":"body"},{"name":"require_priced_model","description":"Refuse a generation whose model carries no price row for every billable token component, with `409 MODEL_NOT_PRICED`. Defaults to `false`, where an unpriced model runs and meters at `cost_usd: null`. The check runs before the provider is called, so a refused turn spends nothing, and covers every target of a model route the turn may fail over to.","required":false,"type":"boolean","in":"body"},{"name":"default_conversation_retrieval","description":"What a conversation that names no `retrieval` of its own does. `none` by default: a turn is embedded because someone asked for the conversation to be retrievable, never merely because it was said. Turns are stored and chunked on both settings.","required":false,"type":"string","in":"body"},{"name":"trace_content_retention_days","description":"How long trace and generation content is kept before the daily retention sweep content-purges it. `null` (the default) disables retention; otherwise an integer >= 1. The sweep uses the same purge path as `DELETE /traces/{trace_id}/content`, so the row survives as an auditable skeleton with `content_redacted_at` set.","required":false,"type":"integer","in":"body"},{"name":"trace_content_mode","description":"Whether trace and generation content is persisted at all. `full` (the default) stores it; `none` is zero-retention — content is never written, for every agent in the project. An agent may tighten this to `none` on its own but cannot loosen a `none` project back to `full`.","required":false,"type":"string","in":"body"}]},"delete-project":{serviceClass:"Projects",operationId:"deleteProject",description:"Deletes a project. Requires admin role. Fails with `409` if the project has any dependent resource, unless `force=true` is passed, in which case those resources are deleted along with the project. Every project-scoped resource counts: agents, ai providers, model routes, tools, ingestion rules, actors, chats, conversations, sessions, generations, traces, datasets, evals, workflows, tasks, triggers, orchestrations and their runs, formations, memory stores, secrets, files, guardrails, quotas, usage history — and the activity, approval, exception and guardrail-evaluation records a project accumulates while it runs. Audit log entries are the one exception: they outlive the project, keeping their record with `project_id` cleared.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"delete",pathParams:["project_id"],queryParams:["force"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID (proj_ prefix)","required":true,"type":"string","in":"path"},{"name":"force","description":"When `true`, deletes all of the project's dependent resources instead of returning `409 PROJECT_HAS_DEPENDENTS`.\n","required":false,"type":"boolean","in":"query"}]},"pause-project":{serviceClass:"Projects",operationId:"pauseProject",description:"Stops everything the project runs, with one call. From the moment it answers, nothing new starts in the project: agent, session and conversation generations, tool-output continuations, chat completions, direct tool calls, orchestration runs, eval runs and trigger firings are refused with `409 PROJECT_PAUSED`, and so is resuming a single run or task the pause holds. What is already in motion is parked rather than discarded: every live orchestration run is paused the way pause-orchestration-run pauses it (a running one parks at its next checkpoint), every open task's automation is paused, schedule triggers stop firing, and queued eval items wait unclaimed. A generation or node already in flight finishes. Reads and configuration writes keep working. Idempotent: pausing a paused project answers it unchanged and keeps the first `reason`. Emits `projects.paused`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"post",pathParams:["project_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID (proj_ prefix)","required":true,"type":"string","in":"path"},{"name":"reason","description":"Why the project is paused. Stored as `pause_reason` and carried onto every run and task the pause parks.","required":false,"type":"string","in":"body"}]},"resume-project":{serviceClass:"Projects",operationId:"resumeProject",description:"Lifts the project's pause and hands back exactly what it held: the runs and tasks the pause parked are resumed (a run the way resume-orchestration-run resumes it, in the background; a task's suppressed dispatch runs as the caller), and schedule triggers fire again from their next occurrence after now — an occurrence that fell due while paused is not fired late. A run or task an operator paused before the project was paused keeps its own pause. Eval runs continue from the item they stopped at. Emits `projects.resumed`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"post",pathParams:["project_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID (proj_ prefix)","required":true,"type":"string","in":"path"}]},"get-project-prices":{serviceClass:"Projects",operationId:"getProjectPrices",description:"Returns the project's per-provider-slug price rows — the middle pricing tier that covers every one of the project's instances of a given provider slug. At cost time a per-provider-instance override wins over these, and these win over the global default. Authorized by the caller's access to the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"get",pathParams:["project_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID (proj_ prefix)","required":true,"type":"string","in":"path"}]},"update-project-prices":{serviceClass:"Projects",operationId:"updateProjectPrices",description:"Upserts project + provider-slug price rows, keyed on (provider, model, effective_from). A row covers all of the project's instances of that provider slug. Authorized by the caller's access to the project. `effective_from` must be in the future once the (provider, model, component) is priced — past prices are immutable, so corrections ship as new future-dated rows. A first price, with no row for the project or the global default, may be dated now or earlier.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/projects",httpMethod:"put",pathParams:["project_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID (proj_ prefix)","required":true,"type":"string","in":"path"},{"name":"prices","description":"","required":true,"type":"array","in":"body"}]},"list-quotas":{serviceClass:"Quotas",operationId:"listQuotas",description:"Returns the quotas defined in a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/quotas",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-quota":{serviceClass:"Quotas",operationId:"createQuota",description:"Creates a project-scoped quota. `requests` is valid for `scope: project`/`api_key`; `tokens` and `cost_usd` are valid for `scope: project`/`agent`/`actor`; `storage_bytes` is valid for `scope: project` only. Any other scope/metric pair is rejected with 400 (no attribution exists to enforce it). An `actor` quota caps one end user's spend, matched from the generation's session; a null `scope_ref` means one budget *per* actor rather than a pooled project total. A `cost_usd` quota may name one `meter_type` to cap; omitting it caps every priced meter. A duplicate quota (same project, scope, scope_ref, metric, window, meter_type) is rejected with 409. `storage_bytes` caps a stored total rather than a windowed one, so it takes `window: current` and every other metric refuses that value (400 either way). It is enforced at the corpus write paths — file upload and create, document create, document ingest and re-ingest, memory create — with `409 QUOTA_STORAGE_EXCEEDED` and no `Retry-After`, since no window reset clears a footprint.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/quotas",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"scope","description":"The scope the quota applies to","required":true,"type":"string","in":"body"},{"name":"scope_ref","description":"Public id of the api key / agent / actor the quota applies to. For `api_key` and `agent` scope, NULL means all entities of that scope type in the project. For `actor` scope, NULL means one budget *per* actor — each end user gets their own allowance — rather than a pooled total across all actors.","required":false,"type":"string","in":"body"},{"name":"metric","description":"The metric being capped","required":true,"type":"string","in":"body"},{"name":"window","description":"The window over which the metric is aggregated. `current` is the only accepted value for storage_bytes (a stored total is not aggregated over time) and is refused for every other metric.","required":true,"type":"string","in":"body"},{"name":"limit","description":"The cap. Must be a positive integer for requests/tokens/storage_bytes (bytes); fractional values are allowed for cost_usd.","required":true,"type":"number","in":"body"},{"name":"mode","description":"enforce blocks with 429 (requests at the middleware, tokens/cost_usd at the pre-generation check); monitor observes without blocking — a breach fires the quota.exceeded webhook and writes a quotas:MonitorBreach audit entry, but the request is let through.","required":false,"type":"string","in":"body"},{"name":"on_unpriced","description":"Only for metric cost_usd (400 on any other metric). What an enforce quota does when the current window is a pricing blackout — several metered llm_tokens events, none of them priced, so the aggregate is 0 however much was actually spent. Platform meters such as compute_execution are read for the aggregate but never for this verdict. block (the default) refuses new generations with 409 QUOTA_UNENFORCEABLE until pricing is configured; allow accepts the unmeasurable spend explicitly. Either way a quota_unpriced exception is filed. monitor-mode quotas never block regardless. A partly priced window is not a blackout: no posture refuses it, it is enforced on its priced total, and it files the same exception.","required":false,"type":"string","in":"body"},{"name":"meter_type","description":"Only for metric cost_usd (400 on any other metric). The meter this cap answers for. Omit it and the cap sums every priced meter, which is the existing behaviour; name one and only that meter's cost counts, so an AI spend cap is not consumed by platform meters the operator prices (and vice versa). Part of the quota's identity, so two meter scopes can share a scope/metric/window and neither conflicts with an unscoped cap. Immutable after creation — replace the quota to change it.","required":false,"type":"string","in":"body"}]},"get-quota":{serviceClass:"Quotas",operationId:"getQuota",description:"Returns a specific quota, including current window usage",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/quotas",httpMethod:"get",pathParams:["quota_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"quota_id","description":"Quota ID","required":true,"type":"string","in":"path"}]},"update-quota":{serviceClass:"Quotas",operationId:"updateQuota",description:"Updates a quota's limit and/or mode. Other fields are immutable.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/quotas",httpMethod:"patch",pathParams:["quota_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"quota_id","description":"Quota ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"New limit","required":false,"type":"number","in":"body"},{"name":"mode","description":"New mode","required":false,"type":"string","in":"body"},{"name":"on_unpriced","description":"New pricing posture. Only for metric cost_usd (400 on any other metric); see the create operation for what block and allow mean.","required":false,"type":"string","in":"body"}]},"delete-quota":{serviceClass:"Quotas",operationId:"deleteQuota",description:"Deletes a quota and drops its window counters",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/quotas",httpMethod:"delete",pathParams:["quota_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"quota_id","description":"Quota ID","required":true,"type":"string","in":"path"}]},"list-secrets":{serviceClass:"Secrets",operationId:"listSecrets",description:"Returns a list of secrets for a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/secrets",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-secret":{serviceClass:"Secrets",operationId:"createSecret",description:"Creates a new encrypted secret in a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/secrets",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"name","description":"Secret name","required":true,"type":"string","in":"body"},{"name":"value","description":"Secret value (will be encrypted)","required":true,"type":"string","in":"body"}]},"get-secret":{serviceClass:"Secrets",operationId:"getSecret",description:"Returns a specific secret",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/secrets",httpMethod:"get",pathParams:["secret_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"secret_id","description":"Secret ID","required":true,"type":"string","in":"path"}]},"update-secret":{serviceClass:"Secrets",operationId:"updateSecret",description:"Updates a secret's name and/or value",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/secrets",httpMethod:"patch",pathParams:["secret_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"secret_id","description":"Secret ID","required":true,"type":"string","in":"path"},{"name":"name","description":"New secret name","required":false,"type":"string","in":"body"},{"name":"value","description":"New secret value","required":false,"type":"string","in":"body"}]},"delete-secret":{serviceClass:"Secrets",operationId:"deleteSecret",description:"Deletes a secret",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/secrets",httpMethod:"delete",pathParams:["secret_id"],queryParams:["force"],headerParams:[],cookieParams:[],flags:[{"name":"secret_id","description":"Secret ID","required":true,"type":"string","in":"path"},{"name":"force","description":"Delete the secret even when AI providers reference it, destroying those providers too. Without it a referenced secret answers SECRET_HAS_DEPENDENTS.","required":false,"type":"boolean","in":"query"}]},"list-sessions":{serviceClass:"Sessions",operationId:"listSessions",description:"Returns sessions the caller can access, optionally filtered by agent, actor and status.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"get",pathParams:[],queryParams:["project_id","agent_id","actor_id","status","tags","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Filter by project public ID. Required for `soat:ResourceTag` policy conditions to narrow the listing: a policy is read per project, so without it the listing is scoped to the projects the caller can access and nothing further.\n","required":false,"type":"string","in":"query"},{"name":"agent_id","description":"Filter by agent public ID","required":false,"type":"string","in":"query"},{"name":"actor_id","description":"Filter by actor public ID","required":false,"type":"string","in":"query"},{"name":"status","description":"Filter by session status (open, closed, or expired)","required":false,"type":"string","in":"query"},{"name":"tags","description":"Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n","required":false,"type":"array","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-session":{serviceClass:"Sessions",operationId:"createSession",description:"Creates a new session for the specified agent, along with the underlying conversation, so the caller only needs this single call to start interacting with the agent. No actor is created: pass `actor_id` to attach an existing actor as the session's end user. When it is omitted the session has no actor, and generations in it carry no end-user attribution — they are not billed to an actor in the usage meter and they match no `actor`-scoped quota.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Agent this session belongs to. With a credential scoped to a project, it may also be an agent another project shares with that project; the session is then that project's.\n","required":true,"type":"string","in":"body"},{"name":"name","description":"Optional session name","required":false,"type":"string","in":"body"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":false,"type":"object","in":"body"},{"name":"actor_id","description":"Optional public ID of an existing actor to use as the user actor. Actors are created separately (POST /actors); this field only links one. Omit it and the session has no end user, so its generations match no actor-scoped quota.\n","required":false,"type":"string","in":"body"},{"name":"auto_generate","description":"When true, automatically triggers generation after each user message.","required":false,"type":"boolean","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as `X-Soat-Context-<key>` headers on every `http`, `mcp` and `builtin` tool call in this session. The header name is the deployment's configured context prefix (`X-Soat-Context-` by default) plus the key verbatim — no character is re-cased. Keys are stored exactly as sent and never case-converted. A key that is not a valid HTTP header name, or two keys that map to the same header, are rejected with `400 INVALID_TOOL_CONTEXT_KEY`. `session_id`, `actor_id` and `actor_external_id` are server-derived and dropped from the stored bag, in any casing. Write-only: no read of a session returns it. It also does not outlive the session: closing or expiring it clears the bag.","required":false,"type":"object","in":"body"},{"name":"inactivity_ttl_seconds","description":"Number of seconds of inactivity after which the session expires. 0 means the session never expires.","required":false,"type":"integer","in":"body"},{"name":"message_delay_seconds","description":"Number of seconds to wait after the last user message before sending to the LLM. Acts as a debounce: each new message resets the timer. null or absent means no delay (immediate processing).\n","required":false,"type":"integer","in":"body"},{"name":"max_context_messages","description":"Maximum number of recent conversation messages sent to the model on each generation, taking precedence over the agent's `max_context_messages`. null or absent defers to the agent.\n","required":false,"type":"integer","in":"body"}]},"get-session":{serviceClass:"Sessions",operationId:"getSession",description:"Returns details of a single session, including a `usage` roll-up of what its generations cost. The listing omits `usage`; for a window, a split by day or model, or one end user across every session, narrow the usage aggregate with `session_id` / `actor_id` instead.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"get",pathParams:["session_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"}]},"update-session":{serviceClass:"Sessions",operationId:"updateSession",description:"Updates the session name and/or status.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"patch",pathParams:["session_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"},{"name":"name","description":"Session name (set to null to clear)","required":false,"type":"string","in":"body"},{"name":"status","description":"Session status","required":false,"type":"string","in":"body"},{"name":"auto_generate","description":"Enable or disable automatic generation after user messages.","required":false,"type":"boolean","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as `X-Soat-Context-<key>` headers on every `http`, `mcp` and `builtin` tool call in this session. The header name is the deployment's configured context prefix (`X-Soat-Context-` by default) plus the key verbatim — no character is re-cased. Keys are stored exactly as sent and never case-converted. A key that is not a valid HTTP header name, or two keys that map to the same header, are rejected with `400 INVALID_TOOL_CONTEXT_KEY`. `session_id`, `actor_id` and `actor_external_id` are server-derived and dropped from the stored bag, in any casing. Write-only: no read of a session returns it. It also does not outlive the session: closing or expiring it clears the bag.","required":false,"type":"object","in":"body"},{"name":"inactivity_ttl_seconds","description":"Number of seconds of inactivity after which the session expires. 0 means the session never expires. Updates the stored TTL; the inactivity clock continues from the last activity timestamp.\n","required":false,"type":"integer","in":"body"},{"name":"message_delay_seconds","description":"Number of seconds to wait after the last user message before sending to the LLM. Acts as a debounce: each new message resets the timer. Set to null to disable the delay.\n","required":false,"type":"integer","in":"body"},{"name":"max_context_messages","description":"Maximum number of recent conversation messages sent to the model on each generation, taking precedence over the agent's `max_context_messages`. Set to null to defer to the agent.\n","required":false,"type":"integer","in":"body"}]},"delete-session":{serviceClass:"Sessions",operationId:"deleteSession",description:"Deletes the session and its underlying conversation and messages. The session's actor is not deleted. Generations and traces produced by the session are not deleted either, since they are not linked to the session or conversation.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"delete",pathParams:["session_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"}]},"add-session-message":{serviceClass:"Sessions",operationId:"addSessionMessage",description:"Saves a user message to the session. When autoGenerate is enabled on the session and no generation is currently in progress, generation is triggered automatically and the response mirrors GenerateSessionResponse. Otherwise returns the saved user message.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"post",pathParams:["session_id"],queryParams:[],headerParams:["Idempotency-Key"],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"},{"name":"Idempotency-Key","description":"The request's deduplication key. Equivalent to `idempotency_key` in the request body; sending both with different values is `400 VALIDATION_FAILED`. A non-empty string of at most 255 characters.","required":false,"type":"string","in":"header"},{"name":"message","description":"User message text","required":false,"type":"string","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as `X-Soat-Context-<key>` headers on every `http`, `mcp` and `builtin` tool call in this generation. The header name is the deployment's configured context prefix (`X-Soat-Context-` by default) plus the key verbatim — no character is re-cased. Keys are never case-converted — they round-trip exactly as sent. An invalid or colliding key is rejected with `400 INVALID_TOOL_CONTEXT_KEY`.","required":false,"type":"object","in":"body"},{"name":"idempotency_key","description":"Optional deduplication key scoped to this session. If a message with the same key already exists in the session, the original message is returned with HTTP 200 and no new message or generation is triggered. Reusing the key with a different `message`, `document_id` or `tool_context` is `409 IDEMPOTENCY_KEY_REUSED`.\n","required":false,"type":"string","in":"body"},{"name":"document_id","description":"Public ID of a document used as the user message content.","required":false,"type":"string","in":"body"}]},"generate-session-response":{serviceClass:"Sessions",operationId:"generateSessionResponse",description:"Triggers the agent to generate a response based on the current conversation. Background by default: returns `202 Accepted` immediately while the generation runs. Pass ?wait=true to block and receive the assistant reply (or a requires_action status if the agent needs client tool outputs) in the response.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"post",pathParams:["session_id"],queryParams:["wait"],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"},{"name":"wait","description":"When omitted or `false` (default), generation runs in the background and `202 Accepted` is returned immediately. Pass `true` to block until the generation settles and receive the result. A tool call, `builtin` or MCP, always waits.","required":false,"type":"boolean","in":"query"},{"name":"model","description":"Optional model override","required":false,"type":"string","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as `X-Soat-Context-<key>` headers on every `http`, `mcp` and `builtin` tool call in this generation. The header name is the deployment's configured context prefix (`X-Soat-Context-` by default) plus the key verbatim — no character is re-cased. Keys are never case-converted — they round-trip exactly as sent. An invalid or colliding key is rejected with `400 INVALID_TOOL_CONTEXT_KEY`.","required":false,"type":"object","in":"body"}]},"submit-session-tool-outputs":{serviceClass:"Sessions",operationId:"submitSessionToolOutputs",description:"Submits client tool outputs for a generation that returned requires_action. The agent continues its loop and returns the final or next requires_action result.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"post",pathParams:["session_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"},{"name":"generation_id","description":"The generation ID from the requires_action response","required":true,"type":"string","in":"body"},{"name":"tool_outputs","description":"","required":true,"type":"array","in":"body"}]},"fork-session":{serviceClass:"Sessions",operationId:"forkSession",description:"Branches a new session from a point in this session's history: same context, different continuation. The fork gets its own conversation whose messages **reference the same documents** as the parent rather than copying them, so there is one stored copy of the content and a retention purge erases it from both. Recorded tool results ride along on those messages and are **replayed** as model input on the fork's next turn — forking never re-invokes a tool, so exploring a \"what if\" cannot send an email or charge a card a second time. The consequence to accept is that a forked turn sees the tool data as it was, not as it is now. The fork is created **inert**: `auto_generate` is false and no generation is triggered. Drive it with the normal message and generate endpoints. The fork has no actor — attach one only if the branch is meant to be driven by the same end user, since `single_session_per_actor` agents allow one open session per actor.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"post",pathParams:["session_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"},{"name":"fork_at_position","description":"The parent conversation `position` to branch after. Messages at positions 0..N are carried into the fork. Omit it to branch at the tip (the whole history).\n","required":false,"type":"integer","in":"body"},{"name":"agent_id","description":"Agent the fork runs against. Defaults to the parent session's agent; overriding it is the point of forking — same context, a different agent or agent version. Must belong to the same project as the session being forked.\n","required":false,"type":"string","in":"body"},{"name":"name","description":"Optional name for the forked session","required":false,"type":"string","in":"body"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":false,"type":"object","in":"body"},{"name":"tool_context","description":"Overrides the parent's `tool_context` on the fork. Omit it and the fork inherits the parent's, so the branch is faithful to the run it came from.\n","required":false,"type":"object","in":"body"}]},"list-session-forks":{serviceClass:"Sessions",operationId:"listSessionForks",description:"Returns the sessions forked directly from this one. One level of lineage: a fork of a fork is listed under its own parent.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"get",pathParams:["session_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-session-tags":{serviceClass:"Sessions",operationId:"getSessionTags",description:"Returns the session's tags object.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"get",pathParams:["session_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"}]},"replace-session-tags":{serviceClass:"Sessions",operationId:"replaceSessionTags",description:"Replaces all tags on the session.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"put",pathParams:["session_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"merge-session-tags":{serviceClass:"Sessions",operationId:"mergeSessionTags",description:"Merges the provided tags into the session's existing tags.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/sessions",httpMethod:"patch",pathParams:["session_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"session_id","description":"Session public ID","required":true,"type":"string","in":"path"},{"name":"tags","description":"Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter, knowledge search, and the IAM `soat:ResourceTag/<key>` condition.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.","required":true,"type":"object","in":"body-root"}]},"list-shares":{serviceClass:"Shares",operationId:"listShares",description:"With `role=publisher` (the default), the shares the project published. With `role=grantee`, the shares addressed to the project and the public shares it accepted, each with the project's own `acceptance`. A public share the project never accepted is not listed.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"get",pathParams:[],queryParams:["project_id","role","resource","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project ID (required if not using project key auth, and always for `role=grantee`)","required":false,"type":"string","in":"query"},{"name":"role","description":"Which side of the shares to list","required":false,"type":"string","in":"query"},{"name":"resource","description":"Only shares of this resource SRN (`role=publisher` only)","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-share":{serviceClass:"Shares",operationId:"createShare",description:"Offers one of the project's resources to another project — or, with `grantee: \"*\"`, to every project — with the listed actions. The share grants nothing until the grantee accepts it. `grantee: \"*\"` is refused with `403 PUBLIC_SHARES_DISABLED` unless the deployment sets `SHARES_ALLOW_PUBLIC=true`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"The publishing project (required if not using project key auth)","required":false,"type":"string","in":"body"},{"name":"resource","description":"The SRN of one resource in the publishing project, `srn:<project_id>:<type>:<id>`. No wildcard.","required":true,"type":"string","in":"body"},{"name":"actions","description":"The actions granted: `tools:CallTool` for a `tool`, `agents:CreateAgentGeneration` for an `agent`. Any other action is `400 VALIDATION_FAILED`.","required":true,"type":"array","in":"body"},{"name":"grantee","description":"The project the share is offered to, or `*` for every project. Never the publishing project itself.","required":true,"type":"string","in":"body"},{"name":"cap","description":"The calls each accepting project may make through the share per fixed window; a call past it is `429 SHARE_CAP_EXCEEDED`. Omit or `null` for no cap.","required":false,"type":"object","in":"body"}]},"get-share":{serviceClass:"Shares",operationId:"getShare",description:"Returns a share with the grantee's view of the resource in `projection`. The publisher reads any of its shares; a grantee project, named by `project_id` or by its scoped credential, reads a share addressed to it or a public one, with its own `acceptance`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"get",pathParams:["share_id"],queryParams:["project_id"],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"},{"name":"project_id","description":"The project the caller reads as. Omit, or name the publisher, for the publisher's view; name a grantee project for its view.","required":false,"type":"string","in":"query"}]},"update-share":{serviceClass:"Shares",operationId:"updateShare",description:"Changes a share's `cap`, as its publisher. The next call through the share reads the new cap; a changed `window` starts a fresh count.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"patch",pathParams:["share_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"},{"name":"cap","description":"The calls each accepting project may make through the share per fixed window; a call past it is `429 SHARE_CAP_EXCEEDED`. `null` removes the cap.","required":false,"type":"object","in":"body"}]},"delete-share":{serviceClass:"Shares",operationId:"deleteShare",description:"Deletes a share and every acceptance of it. Each project whose acceptance was active gets a `share_revoked` activity entry.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"delete",pathParams:["share_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"}]},"accept-share":{serviceClass:"Shares",operationId:"acceptShare",description:"Records the grantee project's acceptance, which is what makes the share grant anything. Idempotent. A project that revoked its own acceptance may accept again; one whose acceptance the publisher revoked answers `403 SHARE_REVOKED` until the publisher deletes that acceptance. A suspended share can be accepted and grants nothing until resumed.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"post",pathParams:["share_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"},{"name":"project_id","description":"The accepting project (required if not using project key auth)","required":false,"type":"string","in":"body"}]},"suspend-share":{serviceClass:"Shares",operationId:"suspendShare",description:"Sets `suspended_at`, which denies every acceptance without removing any. Reversible with resume; nobody re-accepts. Each project with an active acceptance gets a `share_suspended` activity entry. Idempotent.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"post",pathParams:["share_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"}]},"resume-share":{serviceClass:"Shares",operationId:"resumeShare",description:"Clears `suspended_at`. Each project with an active acceptance gets a `share_resumed` activity entry. Idempotent.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"post",pathParams:["share_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"}]},"revoke-share":{serviceClass:"Shares",operationId:"revokeShare",description:"As the publisher, revokes the whole share and every acceptance of it; terminal. Each project whose acceptance was active gets a `share_revoked` activity entry. As a grantee (`project_id`, or a credential scoped to it), revokes that project's own acceptance, which it may accept again later; while the project's own resources still name the shared resource (see `GET /api/v1/shares/{share_id}/references`), that answers `409 SHARE_IN_USE` unless `force=true`. A publisher's revoke is never refused on that account. Idempotent.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"post",pathParams:["share_id"],queryParams:["force"],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"},{"name":"force","description":"As a grantee, revoke even while the project's resources name the shared resource; they degrade as for a revoked share.","required":false,"type":"boolean","in":"query"},{"name":"project_id","description":"The project the caller revokes for. Omit, or name the publisher, to revoke the share; name a grantee project to revoke its acceptance.","required":false,"type":"string","in":"body"}]},"list-share-references":{serviceClass:"Shares",operationId:"listShareReferences",description:"The grantee project's own resources that name the resource the share grants: agents binding a shared tool, pipelines stepping through it, ingestion rules converting with it, orchestrations, triggers and formations naming it. What degrades if the project revokes its acceptance, and what `409 SHARE_IN_USE` lists. The grantee is named by `project_id` or by its scoped credential; the publisher holds none of these and is answered `400`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"get",pathParams:["share_id"],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"},{"name":"project_id","description":"The grantee project whose references to list.","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"list-share-acceptances":{serviceClass:"Shares",operationId:"listShareAcceptances",description:"The projects that accepted the share, for its publisher.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"get",pathParams:["share_id"],queryParams:["status","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"},{"name":"status","description":"Only acceptances in this status","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"revoke-share-acceptance":{serviceClass:"Shares",operationId:"revokeShareAcceptance",description:"The publisher cutting one consumer. The acceptance is kept with `revoked_by: publisher`, and a new accept from that project answers `403 SHARE_REVOKED` until the publisher deletes it. The project gets a `share_revoked` activity entry if the acceptance was active.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"post",pathParams:["share_id","acceptance_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"},{"name":"acceptance_id","description":"Share acceptance ID","required":true,"type":"string","in":"path"}]},"delete-share-acceptance":{serviceClass:"Shares",operationId:"deleteShareAcceptance",description:"Removes one project's acceptance, which lets a project the publisher revoked accept again. The project gets a `share_revoked` activity entry if the acceptance was active.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/shares",httpMethod:"delete",pathParams:["share_id","acceptance_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"share_id","description":"Share ID","required":true,"type":"string","in":"path"},{"name":"acceptance_id","description":"Share acceptance ID","required":true,"type":"string","in":"path"}]},"list-tasks":{serviceClass:"Tasks",operationId:"listTasks",description:"Lists tasks (the board query). Filter by workflow, state, status, automation status, or assignee — `GET /tasks?workflow_id=...&state=...` is one board column.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"get",pathParams:[],queryParams:["project_id","workflow_id","state","status","automation_status","assignee","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"","required":false,"type":"string","in":"query"},{"name":"workflow_id","description":"","required":false,"type":"string","in":"query"},{"name":"state","description":"","required":false,"type":"string","in":"query"},{"name":"status","description":"","required":false,"type":"string","in":"query"},{"name":"automation_status","description":"Filter by the current state's dispatch status. Repeat the parameter to OR values. `none` selects the tasks whose `automation_status` is `null` — the ones that never entered a state with an automation. It is a value a task really holds, so it is a value of the filter too; the parameter's own absence already means \"every task\".\n\nA value outside the enum, empty string included, is a `400`.","required":false,"type":"array","in":"query"},{"name":"assignee","description":"","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-task":{serviceClass:"Tasks",operationId:"createTask",description:"Creates a task bound to a workflow. By default the task is placed in the workflow's initial state; passing `state` places it directly in that named state instead — an alternate entry point for starting a task mid-flow (e.g. \"a new recorte for an existing theme by id\"), rather than re-submitting from the initial state and hoping a guard or similarity gate recognizes it. Entering the resulting state, initial or named, behaves identically: that state's `on_enter` automation fires and its `stalled_after` clock arms.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"","required":false,"type":"string","in":"body"},{"name":"workflow_id","description":"","required":true,"type":"string","in":"body"},{"name":"title","description":"","required":true,"type":"string","in":"body"},{"name":"payload","description":"","required":false,"type":"object","in":"body"},{"name":"assignee","description":"","required":false,"type":"string","in":"body"},{"name":"state","description":"Name of a declared workflow state to create the task in directly, instead of the workflow's `initial` state. Must name a state declared on the workflow, or the request is rejected with `TASK_STATE_NOT_FOUND` (400). Defaults to the `initial` state.","required":false,"type":"string","in":"body"},{"name":"tool_context","description":"Key-value pairs forwarded as `X-Soat-Context-<key>` headers on every `http`, `mcp` and `builtin` tool call made by this task's automation dispatches — the agent generations a state's `on_enter` starts, and the agent nodes of any orchestration run it starts. The header name is `X-Soat-Context-` plus the key verbatim; no character is re-cased.\nCreation is the task's first move, so this is the bag the entry state's `on_enter` runs with. Each transition may replace it (see `TransitionTaskRequest.tool_context`).\nThe reserved identity keys (`session_id`, `actor_id`, `actor_external_id`) are stripped in any casing and re-derived server-side, so a task-dispatched generation cannot forge them. A key outside the HTTP header-name grammar is rejected with `INVALID_TOOL_CONTEXT_KEY` (400).\nWrite-only: the stored bag is never returned by any task read, and it is cleared when the task reaches a terminal state.","required":false,"type":"object","in":"body"},{"name":"metadata","description":"Caller-supplied key/value metadata attached to the task record for attribution — which of your own tenants the task belongs to, the ticket that raised it, the import batch that created it. Round-trips verbatim on every read of the task, the list included, and survives every transition (a transition supplies no metadata of its own).\n\nThe bag is caller-owned and no key is reserved: everything the engine decides about a task (`state`, `status`, `workflow_version`, `last_result`, `active_dispatch`, the automation fields) is a field of its own and cannot be written from here.\n\nPrefer this over `payload` for anything that is not task data: `payload` is read by every guard as `task.payload` and may be written by the workflow's declared `payload_writes`, so a label parked there is neither invisible to the state machine nor safe from it. A non-object is rejected with `400 VALIDATION_FAILED` and no task is created.","required":false,"type":"object","in":"body"}]},"get-task":{serviceClass:"Tasks",operationId:"getTask",description:"Retrieves a task, including its active dispatch and automation status.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"get",pathParams:["task_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"task_id","description":"","required":true,"type":"string","in":"path"}]},"update-task":{serviceClass:"Tasks",operationId:"updateTask",description:"Updates a task's payload, title, or assignee. `state` is never directly writable — move it with a transition; sending a `state` field is rejected as an unknown field (`VALIDATION_FAILED`). `payload` is shallow-merged over the existing payload (PATCH semantics): keys the request omits are preserved. The payload is caller-owned; the automation result lives in the read-only `last_result` field, which no patch can reach. The merged payload is validated against the workflow's `payload_schema`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"patch",pathParams:["task_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"task_id","description":"","required":true,"type":"string","in":"path"},{"name":"title","description":"","required":false,"type":"string","in":"body"},{"name":"payload","description":"Partial payload, shallow-merged over the existing payload. Omitted keys are preserved; provided keys overwrite. The merged result must satisfy the workflow's payload_schema.","required":false,"type":"object","in":"body"},{"name":"assignee","description":"","required":false,"type":"string","in":"body"}]},"delete-task":{serviceClass:"Tasks",operationId:"deleteTask",description:"Deletes a task. Its transition history cascades.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"delete",pathParams:["task_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"task_id","description":"","required":true,"type":"string","in":"path"}]},"transition-task":{serviceClass:"Tasks",operationId:"transitionTask",description:"Fires a named transition on a task. The transition must exist in the workflow and be valid from the task's current state; its guard must pass. This is the single path every state change routes through. A transition declaring `requires_approval` does not move the task — it parks a pending ApprovalItem and returns the task with `pending_transition` set; the move applies only when the approval is approved.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"post",pathParams:["task_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"task_id","description":"","required":true,"type":"string","in":"path"},{"name":"transition","description":"","required":true,"type":"string","in":"body"},{"name":"note","description":"","required":false,"type":"string","in":"body"},{"name":"tool_context","description":"Caller context for the automation dispatches the task makes from here on, forwarded as `X-Soat-Context-<key>` headers on their tool calls.\nSupplying it **replaces** the task's stored bag wholesale; omitting it keeps the current one, so the context follows whoever last moved the task and survives every move that does not speak about it — including an approval gate, a retry, and an automation hop. Send an empty object to clear it without closing the task.\nThe reserved identity keys (`session_id`, `actor_id`, `actor_external_id`) are stripped in any casing and re-derived server-side. A key outside the HTTP header-name grammar is rejected with `INVALID_TOOL_CONTEXT_KEY` (400).\nWrite-only: never returned by a task read, and cleared when the transition closes the task.","required":false,"type":"object","in":"body"}]},"pause-task":{serviceClass:"Tasks",operationId:"pauseTask",description:"Pauses a task's automation. While a pause is in force no state's `on_enter` dispatches and no retry chain continues — an agent generation, a tool call and a sub-orchestration alike — so the task stops spending without losing its place. A workflow has no run object, so this is the workflow half of pause-orchestration-run: the pause lands on the instance, which is the task. Transitions are deliberately still allowed — a move costs nothing while every dispatch it would start is suppressed — so a board stays usable under a pause. Entering a state whose dispatch is suppressed records `automation_status: paused`, which resume-task reads to know that state still owes its work. A dispatch already in flight is left to finish, and its outcome still routes; only what would start after it is suppressed. Idempotent: pausing an already-paused task answers with it unchanged.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"post",pathParams:["task_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"task_id","description":"","required":true,"type":"string","in":"path"},{"name":"reason","description":"Why the task is being paused, surfaced on `pause_reason`.","required":false,"type":"string","in":"body"}]},"resume-task":{serviceClass:"Tasks",operationId:"resumeTask",description:"Lifts a task's pause. When the pause suppressed the current state's `on_enter` — `automation_status: paused` — that dispatch is started now, as the caller resuming rather than as whoever last moved the task. A state whose dispatch had already completed, or that declares none, is left alone, so a resume never re-spends work the pause did not stop. This is the only way a pause is lifted; a task that is merely idle is advanced by firing a transition instead.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"post",pathParams:["task_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"task_id","description":"","required":true,"type":"string","in":"path"}]},"get-task-history":{serviceClass:"Tasks",operationId:"getTaskHistory",description:"Returns the append-only transition history of a task.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tasks",httpMethod:"get",pathParams:["task_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"task_id","description":"","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"list-tools":{serviceClass:"Tools",operationId:"listTools",description:"Returns all tools in the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tools",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID to filter by","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-tool":{serviceClass:"Tools",operationId:"createTool",description:"Creates a new tool in the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tools",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project","required":false,"type":"string","in":"body"},{"name":"name","description":"Tool name","required":true,"type":"string","in":"body"},{"name":"type","description":"Tool type (default http)","required":false,"type":"string","in":"body"},{"name":"description","description":"What the tool does","required":false,"type":"string","in":"body"},{"name":"parameters","description":"JSON Schema for tool input","required":false,"type":"object","in":"body"},{"name":"execute","description":"Execution config for http tools. Supported fields: `url` (required), `method` (default `POST`), `headers`, `body_mode` and `response_mode`. The `url` may contain `{paramName}` placeholders (e.g. `/users/{userId}`) that are replaced at call time with the corresponding tool argument value (URL-encoded). Arguments consumed as path parameters are excluded from the query string and request body. `body_mode` is `json` (default) or `multipart`. In `multipart` mode the merged tool arguments are sent as a `multipart/form-data` body: scalar fields become plain form fields and a field shaped like `{ content_type, filename, data_base64 }` is decoded from base64 and attached as a file part (the hardcoded `Content-Type: application/json` is dropped so `fetch` sets the multipart boundary itself). `response_mode` is `json` (default: the body parsed as JSON, else returned as text) or `base64`, which returns a 2xx body as `{ content_type, filename, data_base64 }` — the same file shape `multipart` accepts, so a pipeline step can relay a downloaded file. `filename` comes from `Content-Disposition` and is omitted when the target names none; a body over `TOOL_RESPONSE_MAX_BYTES` is `502 TOOL_RESPONSE_TOO_LARGE`.\n\n`auth` adds a computed request credential, for targets whose `Authorization` value cannot be expressed as a static header. Supported `auth.type` values:\n\n- `aws_sigv4` — signs the request with AWS Signature Version 4. Requires `region`, `service`, `access_key_id` and `secret_access_key`; `session_token` is optional (temporary credentials). Incompatible with `body_mode: multipart`, whose body bytes are not known at signing time.\n- `gcp_service_account` — mints a Google OAuth 2.0 access token from a signed service account assertion and sends it as a bearer token. Requires `credentials` (the service account key file JSON, as a string) and `scopes` (a non-empty array). Tokens are cached per service account and scope set until shortly before they expire.\n\nCredential fields accept `{{secret:...}}` references and should use them — a tool is readable by anyone who can `GET /tools`, and the stored reference is what is echoed back, never the resolved value.\n\nA credential written as a **literal** is stored and still sent on every call, but it is masked on every read: `secret_access_key`, `session_token` and `credentials` under `auth`, and any credential-named `headers` value (`Authorization`, `Cookie`, or a name containing `api-key`/`token`/`secret`/`password`), come back as `{\"no_echo\": true}`. The mask is an object rather than a string so a read-edit-write round trip fails the schema check instead of writing the placeholder in as the credential. A value carrying a `{{secret:...}}` reference is the wiring, not the credential, and stays readable.\n\n`headers` values additionally accept `{{context:<key>}}` references, resolved per call from the caller's `tool_context`, so a per-user credential can be placed in the real header the target expects (`Authorization: Bearer {{context:ocaToken}}`) instead of only in an `X-Soat-Context-<key>` header. Valid **only** inside `headers` — a context value is caller-supplied, so it may not steer the `url` — and a key missing from the `tool_context` at call time fails the tool call with `MISSING_TOOL_CONTEXT_KEY` rather than sending an empty credential. See the Tool Context reference.\n","required":false,"type":"object","in":"body"},{"name":"mcp","description":"MCP server config (`url`, `headers`). `headers` values accept `{{secret:...}}` and `{{context:<key>}}` references, resolved right before the outbound MCP request; `url` accepts `{{secret:...}}` only. A literal credential in `headers` is masked on read, exactly as in `execute`.","required":false,"type":"object","in":"body"},{"name":"actions","description":"Allowlist of actions. For `builtin` tools: SOAT platform action names (required). For `mcp` tools: an optional allowlist of MCP tool names to scope the server surface — omit or set `null` to expose every tool the MCP server offers. Ignored for other tool types.","required":false,"type":"array","in":"body"},{"name":"denied_actions","description":"For `mcp` tools: an optional denylist of MCP tool names to hide. Applied after `actions` and taking precedence over it. Use it to scope a read+write MCP server read-only by denying just the write tools. Omit or set `null` to deny nothing. Ignored for other tool types.","required":false,"type":"array","in":"body"},{"name":"context_keys","description":"Optional allowlist of `tool_context` keys that may be forwarded to this tool as prefixed context headers (`X-Soat-Context-<key>` by default). When `null` or omitted, every key in the caller's `tool_context` is forwarded. When set, only the listed keys are, so a per-user credential in `tool_context` can be confined to the tools that need it; `[]` forwards none. The server-pinned identity keys (`session_id`, `actor_id`, `actor_external_id`) are always forwarded. A key consumed by a `{{context:<key>}}` token in this tool's own headers is substituted regardless of this list — the tool declared that header itself.","required":false,"type":"array","in":"body"},{"name":"preset_parameters","description":"Fixed parameters pinned on every call this tool makes, whatever its type. Keys matching fields in the input schema are removed from the schema shown to the model, and a pinned value wins over one the model or a direct caller supplies for the same key.\n\nValues accept `{{context:<key>}}` references, resolved per call from the caller's `tool_context`, so a pin can be the run's own value — the one account this run may act on — rather than one fixed when the tool was created. A resolved value is retyped to the parameter's declared schema type; a key missing from the call's `tool_context` fails the call with `MISSING_TOOL_CONTEXT_KEY` rather than sending the literal placeholder. `{{secret:...}}` is not resolved here. See the Tool Context reference.","required":false,"type":"object","in":"body"},{"name":"pipeline","description":"Pipeline definition for `pipeline` tools. See the `pipeline` field on the Tool schema for the full structure.","required":false,"type":"object","in":"body"},{"name":"output_mapping","description":"Universal JSON Logic mapping applied to the tool's raw result. See the `output_mapping` field on the Tool schema for details.","required":false,"type":"object","in":"body"},{"name":"guardrail_ids","description":"Guardrails attached at the tool scope.","required":false,"type":"array","in":"body"}]},"get-tool":{serviceClass:"Tools",operationId:"getTool",description:"Returns a single tool by ID. A credential scoped to a project the tool is shared with, through an accepted share, reads its `id`, `name`, `description` and `parameters` only.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tools",httpMethod:"get",pathParams:["tool_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"tool_id","description":"","required":true,"type":"string","in":"path"}]},"update-tool":{serviceClass:"Tools",operationId:"updateTool",description:"Updates an existing tool.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tools",httpMethod:"patch",pathParams:["tool_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"tool_id","description":"","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"type","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"parameters","description":"","required":false,"type":"object","in":"body"},{"name":"execute","description":"Execution config for http tools. Supported fields: `url` (required), `method` (default `POST`), `headers`, `body_mode` and `response_mode`. The `url` may contain `{paramName}` placeholders (e.g. `/users/{userId}`) that are replaced at call time with the corresponding tool argument value (URL-encoded). Arguments consumed as path parameters are excluded from the query string and request body. `body_mode` is `json` (default) or `multipart`. In `multipart` mode the merged tool arguments are sent as a `multipart/form-data` body: scalar fields become plain form fields and a field shaped like `{ content_type, filename, data_base64 }` is decoded from base64 and attached as a file part (the hardcoded `Content-Type: application/json` is dropped so `fetch` sets the multipart boundary itself). `response_mode` is `json` (default: the body parsed as JSON, else returned as text) or `base64`, which returns a 2xx body as `{ content_type, filename, data_base64 }` — the same file shape `multipart` accepts, so a pipeline step can relay a downloaded file. `filename` comes from `Content-Disposition` and is omitted when the target names none; a body over `TOOL_RESPONSE_MAX_BYTES` is `502 TOOL_RESPONSE_TOO_LARGE`.\n\n`auth` adds a computed request credential, for targets whose `Authorization` value cannot be expressed as a static header. Supported `auth.type` values:\n\n- `aws_sigv4` — signs the request with AWS Signature Version 4. Requires `region`, `service`, `access_key_id` and `secret_access_key`; `session_token` is optional (temporary credentials). Incompatible with `body_mode: multipart`, whose body bytes are not known at signing time.\n- `gcp_service_account` — mints a Google OAuth 2.0 access token from a signed service account assertion and sends it as a bearer token. Requires `credentials` (the service account key file JSON, as a string) and `scopes` (a non-empty array). Tokens are cached per service account and scope set until shortly before they expire.\n\nCredential fields accept `{{secret:...}}` references and should use them — a tool is readable by anyone who can `GET /tools`, and the stored reference is what is echoed back, never the resolved value.\n\nA credential written as a **literal** is stored and still sent on every call, but it is masked on every read: `secret_access_key`, `session_token` and `credentials` under `auth`, and any credential-named `headers` value (`Authorization`, `Cookie`, or a name containing `api-key`/`token`/`secret`/`password`), come back as `{\"no_echo\": true}`. The mask is an object rather than a string so a read-edit-write round trip fails the schema check instead of writing the placeholder in as the credential. A value carrying a `{{secret:...}}` reference is the wiring, not the credential, and stays readable.\n\n`headers` values additionally accept `{{context:<key>}}` references, resolved per call from the caller's `tool_context`, so a per-user credential can be placed in the real header the target expects (`Authorization: Bearer {{context:ocaToken}}`) instead of only in an `X-Soat-Context-<key>` header. Valid **only** inside `headers` — a context value is caller-supplied, so it may not steer the `url` — and a key missing from the `tool_context` at call time fails the tool call with `MISSING_TOOL_CONTEXT_KEY` rather than sending an empty credential. See the Tool Context reference.\n","required":false,"type":"object","in":"body"},{"name":"mcp","description":"MCP server config (`url`, `headers`). `headers` values accept `{{secret:...}}` and `{{context:<key>}}` references, resolved right before the outbound MCP request; `url` accepts `{{secret:...}}` only. A literal credential in `headers` is masked on read, exactly as in `execute`.","required":false,"type":"object","in":"body"},{"name":"actions","description":"Allowlist of actions. For `builtin` tools: SOAT platform action names. For `mcp` tools: an optional allowlist of MCP tool names to scope the server surface (`null` exposes every tool). Ignored for other tool types.","required":false,"type":"array","in":"body"},{"name":"denied_actions","description":"For `mcp` tools: an optional denylist of MCP tool names to hide. Applied after `actions` and taking precedence over it. Use it to scope a read+write MCP server read-only by denying just the write tools. `null` denies nothing. Ignored for other tool types.","required":false,"type":"array","in":"body"},{"name":"context_keys","description":"Optional allowlist of `tool_context` keys that may be forwarded to this tool as prefixed context headers (`X-Soat-Context-<key>` by default). When `null` or omitted, every key in the caller's `tool_context` is forwarded. When set, only the listed keys are, so a per-user credential in `tool_context` can be confined to the tools that need it; `[]` forwards none. The server-pinned identity keys (`session_id`, `actor_id`, `actor_external_id`) are always forwarded. A key consumed by a `{{context:<key>}}` token in this tool's own headers is substituted regardless of this list — the tool declared that header itself.","required":false,"type":"array","in":"body"},{"name":"preset_parameters","description":"Fixed parameters pinned on every call this tool makes, whatever its type. Keys matching fields in the input schema are removed from the schema shown to the model, and a pinned value wins over one the model or a direct caller supplies for the same key.\n\nValues accept `{{context:<key>}}` references, resolved per call from the caller's `tool_context`, so a pin can be the run's own value — the one account this run may act on — rather than one fixed when the tool was created. A resolved value is retyped to the parameter's declared schema type; a key missing from the call's `tool_context` fails the call with `MISSING_TOOL_CONTEXT_KEY` rather than sending the literal placeholder. `{{secret:...}}` is not resolved here. See the Tool Context reference.","required":false,"type":"object","in":"body"},{"name":"pipeline","description":"Pipeline definition for `pipeline` tools. See the `pipeline` field on the Tool schema for the full structure.","required":false,"type":"object","in":"body"},{"name":"output_mapping","description":"Universal JSON Logic mapping applied to the tool's raw result. See the `output_mapping` field on the Tool schema for details.","required":false,"type":"object","in":"body"},{"name":"guardrail_ids","description":"Guardrails attached at the tool scope.","required":false,"type":"array","in":"body"}]},"delete-tool":{serviceClass:"Tools",operationId:"deleteTool",description:"Deletes a tool by ID. A tool that is a decider's backend is refused with `409 TOOL_HAS_DEPENDENTS`, and so is one another project has accepted a share of, or one of its own project's ingestion rules converts with, until `force=true`. Every share of the tool is revoked when it is deleted; an ingestion rule keeps its `tool_id` and fails the documents it matches with `CONVERTER_FAILED` until repointed.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tools",httpMethod:"delete",pathParams:["tool_id"],queryParams:["force"],headerParams:[],cookieParams:[],flags:[{"name":"tool_id","description":"","required":true,"type":"string","in":"path"},{"name":"force","description":"Delete the tool even when another project has accepted a share of it, revoking those shares, or an ingestion rule converts with it. A decider backend still refuses.","required":false,"type":"boolean","in":"query"}]},"call-tool":{serviceClass:"Tools",operationId:"callTool",description:"Directly invokes a tool and returns its output. Supported for `http`, `builtin`, `mcp`, and `pipeline` tools. `client` tools cannot be invoked server-side and will return 422. A `pipeline` tool runs its declared steps in order and returns the mapped `output` (or the last step's output); `action` is ignored and `input` is the pipeline input. For `builtin` and `mcp` tools the `action` field is required and identifies which action (SOAT) or tool name (MCP) to invoke. For `http` tools `action` is ignored. When a `builtin` or `mcp` tool declares an `actions` allowlist, an action outside it is rejected with `400 VALIDATION_FAILED` (\"not available on this tool\") before any outbound request is made. `preset_parameters` stored on the tool are pinned over the caller-supplied `input` before execution: a key the tool presets keeps its preset value even when `input` sets it. Keys the presets do not name are taken from `input` as sent. Guardrails attached to the tool or to its project adjudicate the call before dispatch, composing project + tool scope. A call this route cannot await a decision on — class C (human sign-off), class D, or a class-B tripwire — is refused with `422 TOOL_DISPATCH_FAILED`, whose `meta` carries the `tool_id` and the `outcome`. A `pipeline` tool is adjudicated before its first step runs, and every step is adjudicated as the call of that tool it is. A credential scoped to a project the tool is shared with, through an accepted share, calls it in that project: the calling project's guardrails adjudicate it, it is metered there with `publisher_project_id`, and the tool receives the calling project as the `calling_project_id` tool context key.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/tools",httpMethod:"post",pathParams:["tool_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"tool_id","description":"","required":true,"type":"string","in":"path"},{"name":"action","description":"For `builtin` tools: the action name (must be in the tool's `actions` list). For `mcp` tools: the MCP tool name to invoke (must be in the tool's `actions` allowlist when one is set, and must not be in its `denied_actions` denylist). Ignored for `http` tools.\n","required":false,"type":"string","in":"body"},{"name":"input","description":"Input parameters for the tool call. These are merged with the tool's `preset_parameters` before execution; a preset value wins over the same key sent here.\n","required":false,"type":"object","in":"body"},{"name":"tool_context","description":"Key/value context for this call, forwarded to the tool as `X-Soat-Context-<key>` request headers and resolving any `{{context:<key>}}` token in the tool's `execute.headers`, `mcp.headers` or `preset_parameters`. Narrowed by the tool's `context_keys` allowlist when it sets one.\nThis route has no session, so it stamps no server-derived identity: the reserved keys `session_id`, `actor_id` and `actor_external_id` are dropped from this bag (in any casing) rather than forwarded, so a downstream tool can still trust that a context header naming one is server-derived. Every other key becomes an HTTP header name and must match that grammar, or the call fails with `INVALID_TOOL_CONTEXT_KEY`.\n","required":false,"type":"object","in":"body"}]},"list-traces":{serviceClass:"Traces",operationId:"listTraces",description:"Returns a paginated list of execution traces for the project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/traces",httpMethod:"get",pathParams:[],queryParams:["project_id","parent_trace_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID to filter by","required":false,"type":"string","in":"query"},{"name":"parent_trace_id","description":"Only the direct children of this trace. A trace naming nothing the caller can read matches nothing rather than widening the listing.","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-trace":{serviceClass:"Traces",operationId:"getTrace",description:"Returns a single trace by ID.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/traces",httpMethod:"get",pathParams:["trace_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trace_id","description":"Public ID of the trace","required":true,"type":"string","in":"path"}]},"get-trace-tree":{serviceClass:"Traces",operationId:"getTraceTree",description:"Returns the full execution tree rooted at the given trace (or its root if the given trace is a child). Each node represents one agent's execution session. The `children` array contains traces triggered by sub-agent tool calls from that trace. A node's generations are listed by `GET /api/v1/generations?trace_id=`, and a node's direct children page by `GET /api/v1/traces?parent_trace_id=`.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/traces",httpMethod:"get",pathParams:["trace_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trace_id","description":"Public ID of any trace in the tree (root or child)","required":true,"type":"string","in":"path"}]},"purge-trace-content":{serviceClass:"Traces",operationId:"purgeTraceContent",description:"Deletes the trace's steps object from storage and clears its content columns (`file_id`, `error`), cascading to every descendant trace and to all of their generations. A descendant holds its own steps object covering the same run, so the cascade is what makes the erasure complete rather than merely partial. The rows survive as auditable skeletons with `content_redacted_at` set — ids, timestamps, step counts, and the generations' usage-attribution fields are preserved, because the billing and audit ledger must outlive a tenant's erasure of the content. A purged trace therefore reads back as a skeleton, not a 404: a 404 would prove nothing. Idempotent — purging an already-purged trace succeeds and leaves the original `content_redacted_at` in place.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/traces",httpMethod:"delete",pathParams:["trace_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trace_id","description":"Public ID of the trace","required":true,"type":"string","in":"path"}]},"list-triggers":{serviceClass:"Triggers",operationId:"listTriggers",description:"Lists triggers. Filter by project, starter type, or target type.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"get",pathParams:[],queryParams:["project_id","type","target_type","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"","required":false,"type":"string","in":"query"},{"name":"type","description":"","required":false,"type":"string","in":"query"},{"name":"target_type","description":"","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-trigger":{serviceClass:"Triggers",operationId:"createTrigger",description:"Creates a new trigger for a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project. Optional when authenticating with a project-scoped API key, which defaults to the key's project; required otherwise.","required":false,"type":"string","in":"body"},{"name":"name","description":"","required":true,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"type","description":"","required":true,"type":"string","in":"body"},{"name":"target_type","description":"","required":true,"type":"string","in":"body"},{"name":"target_id","description":"","required":true,"type":"string","in":"body"},{"name":"action","description":"Tool targets only — the action for builtin/mcp tools","required":false,"type":"string","in":"body"},{"name":"input","description":"","required":false,"type":"object","in":"body"},{"name":"tool_context","description":"Caller context every firing forwards to the run it starts, so an agent whose tools authorize through `{{context:<key>}}` can be put on a schedule — a firing has no request to carry one. Each key is forwarded as one `X-Soat-Context-<key>` header.\n\n**Write-only.** It is accepted here and never returned on a read, so the record cannot be used to recover a value.\n\nA value may be a `{{secret:sec_...}}` reference, which keeps the credential in the [secret](/docs/modules/secrets) store and leaves only its id on the trigger; it is resolved at fire time, so rotating the secret changes the next firing without touching the trigger. A reference is refused here rather than at fire time when it is not in that id form (a secret's name does not resolve) or names a secret that does not exist in this project.\n","required":false,"type":"object","in":"body"},{"name":"cron","description":"5-field cron expression (UTC). Required when type is schedule","required":false,"type":"string","in":"body"},{"name":"event_pattern","description":"Internal-event subscription pattern. Required when type is event, rejected otherwise. `*` matches every event, `prefix.*` a namespace, or give an exact event name such as `documents.ingested`","required":false,"type":"string","in":"body"},{"name":"active","description":"","required":false,"type":"boolean","in":"body"},{"name":"policy_id","description":"","required":false,"type":"string","in":"body"}]},"get-trigger":{serviceClass:"Triggers",operationId:"getTrigger",description:"Retrieves the details of a specific trigger",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"get",pathParams:["trigger_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trigger_id","description":"","required":true,"type":"string","in":"path"}]},"update-trigger":{serviceClass:"Triggers",operationId:"updateTrigger",description:"Updates an existing trigger's configuration. The type is immutable. Changing `target_type` or `target_id` re-checks the target-start action (`orchestrations:StartRun`, `agents:CreateAgentGeneration` or `tools:CallTool`) against the resulting target, because a firing runs with the trigger creator's authority rather than the updater's. A caller who could not start the new target is answered `403` and the trigger keeps the target it had.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"patch",pathParams:["trigger_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trigger_id","description":"","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"target_type","description":"","required":false,"type":"string","in":"body"},{"name":"target_id","description":"","required":false,"type":"string","in":"body"},{"name":"action","description":"","required":false,"type":"string","in":"body"},{"name":"input","description":"","required":false,"type":"object","in":"body"},{"name":"tool_context","description":"Replaces the stored bag; `null` clears it. Write-only and resolved at fire time — see `CreateTriggerRequest.tool_context`.\n","required":false,"type":"object","in":"body"},{"name":"cron","description":"","required":false,"type":"string","in":"body"},{"name":"event_pattern","description":"","required":false,"type":"string","in":"body"},{"name":"active","description":"","required":false,"type":"boolean","in":"body"},{"name":"policy_id","description":"","required":false,"type":"string","in":"body"}]},"delete-trigger":{serviceClass:"Triggers",operationId:"deleteTrigger",description:"Deletes a trigger",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"delete",pathParams:["trigger_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trigger_id","description":"","required":true,"type":"string","in":"path"}]},"fire-trigger":{serviceClass:"Triggers",operationId:"fireTrigger",description:"Fires a trigger synchronously and returns the terminal firing record. The firing itself always settles here; an `eval` target's run is queued rather than executed inline, so the record names a `queued` run to poll.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"post",pathParams:["trigger_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trigger_id","description":"","required":true,"type":"string","in":"path"},{"name":"input","description":"Fire-time input, shallow-merged over the trigger's static input. For `eval` targets it may carry `agent_version` and `baseline_run_id`.","required":false,"type":"object","in":"body"},{"name":"tool_context","description":"Fire-time caller context, shallow-merged per key over the trigger's stored `tool_context`. A manual fire has a caller, so this is the one path that can supply a value without storing it.\n\nForwarded exactly as written: a `{{secret:...}}` reference is resolved only in the trigger's stored bag, never in one supplied here.\n","required":false,"type":"object","in":"body"}]},"get-trigger-secret":{serviceClass:"Triggers",operationId:"getTriggerSecret",description:"Retrieves the signing secret for a webhook trigger",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"get",pathParams:["trigger_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trigger_id","description":"","required":true,"type":"string","in":"path"}]},"rotate-trigger-secret":{serviceClass:"Triggers",operationId:"rotateTriggerSecret",description:"Rotates the signing secret for a webhook trigger",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"post",pathParams:["trigger_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"trigger_id","description":"","required":true,"type":"string","in":"path"}]},"list-trigger-firings":{serviceClass:"Triggers",operationId:"listTriggerFirings",description:"Lists firings for a trigger (trigger_id is required).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"get",pathParams:[],queryParams:["trigger_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"trigger_id","description":"Trigger to list firings for (trg_...)","required":true,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-trigger-firing":{serviceClass:"Triggers",operationId:"getTriggerFiring",description:"Retrieves the details of a specific trigger firing",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/triggers",httpMethod:"get",pathParams:["firing_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"firing_id","description":"","required":true,"type":"string","in":"path"}]},"list-usage-events":{serviceClass:"Usage",operationId:"listUsageEvents",description:"Returns the raw usage events the caller can access, most recent first, narrowed by any combination of the same filters the aggregate takes — agent, generation, trace, actor, session, AI provider, orchestration run, orchestration, tool, trigger, action, outcome, meter type, model or `source` — so a rollup and the events behind it are addressed the same way. Each event is the per-generation token usage as reported by the provider, for audit and reconciliation. An id filter matches the id the event recorded, so it still selects the spend of an agent, provider, session or tool since deleted. An id no event in scope recorded yields an empty page rather than dropping the filter, so a mistyped narrowing can never widen the list past what was asked for.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/usage",httpMethod:"get",pathParams:[],queryParams:["agent_id","generation_id","trace_id","actor_id","session_id","ai_provider_id","orchestration_run_id","orchestration_id","model","trigger_id","action_id","tool_id","publisher_project_id","outcome","meter_type","source","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"agent_id","description":"Filter by agent public ID","required":false,"type":"string","in":"query"},{"name":"generation_id","description":"Filter by generation public ID","required":false,"type":"string","in":"query"},{"name":"trace_id","description":"Filter by trace public ID","required":false,"type":"string","in":"query"},{"name":"actor_id","description":"Filter by the actor (end user) the usage is attributed to.\n","required":false,"type":"string","in":"query"},{"name":"session_id","description":"Filter by the session the usage was produced in.\n","required":false,"type":"string","in":"query"},{"name":"ai_provider_id","description":"Filter by the provider record the spend was billed against.\n","required":false,"type":"string","in":"query"},{"name":"orchestration_run_id","description":"Filter by the orchestration run that metered the event.\n","required":false,"type":"string","in":"query"},{"name":"orchestration_id","description":"Filter by orchestration — the events of the runs it started itself, not the subtree its `loop` or `sub_orchestration` nodes started. An orchestration that does not exist in scope yields an empty page.\n","required":false,"type":"string","in":"query"},{"name":"model","description":"Filter by the as-billed SKU the event recorded. Matched as recorded, so it still selects events whose provider has since been deleted.\n","required":false,"type":"string","in":"query"},{"name":"trigger_id","description":"Filter by the trigger that initiated the generation","required":false,"type":"string","in":"query"},{"name":"action_id","description":"Filter by logical action id","required":false,"type":"string","in":"query"},{"name":"tool_id","description":"Filter by the tool a `tool_execution` event metered, including a tool another project shares with one in scope.\n","required":false,"type":"string","in":"query"},{"name":"publisher_project_id","description":"Filter to the calls made through a share of a resource the named project owns.\n","required":false,"type":"string","in":"query"},{"name":"outcome","description":"Filter by how a `tool_execution` call ended: `ok`, `error` or `timeout`. Every other meter carries no outcome and is matched by none of them.\n","required":false,"type":"string","in":"query"},{"name":"meter_type","description":"Filter by meter type (e.g. `llm_tokens`, `compute_execution`, `api_request`, `storage`, `tool_execution`)\n","required":false,"type":"string","in":"query"},{"name":"source","description":"Filter by what the spend was incurred for. `eval` is an eval run's item generations, `eval_judge` an `llm_judge` scorer's own completion and `eval_scorer` a `tool` scorer's call, so verification spend is `source` in (`eval`, `eval_judge`, `eval_scorer`); `decider` is a decision's evaluation; `embedding` is any embedding call. Ordinary agent traffic carries no source and is matched by none of them.\n","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-usage-aggregate":{serviceClass:"Usage",operationId:"getUsageAggregate",description:"Returns a project's usage rolled up over an optional `[from, to]` time window, optionally bucketed by a single dimension and narrowed by any combination of sixteen filters — session, actor, agent, AI provider, orchestration run, orchestration, generation, trace, tool, publisher project, meter type, model, source, trigger, action and outcome. That is how \"what did this conversation cost\", \"what has this end user cost\" and \"what did this agent cost\" are answered. Each group and the grand total carry an `event_count`, summed token counts, a measured `quantity` per component (so infra meters report their real amount, not zeros), and `cost_usd` (null when no event in the bucket was priced). This is the per-project cost-by-range/by-category query — a monthly figure without scanning raw usage events client-side. Narrowings intersect and apply to the whole rollup — every bucket, `totals` and `totals.distinct` alike — so each composes with any `group_by`, and all sixteen are echoed back under `filters`. The ten naming a resource match the id the event recorded, so a deleted resource's spend stays selectable; an id no event recorded empties the rollup rather than being dropped: a mistyped id must never read back as the project's whole spend. `orchestration_id` is the exception — the event records no orchestration, so a deleted one empties the rollup. The other six are matched against the value the event recorded. `groups` is paginated. Its `total` is the number of distinct buckets in the window — bucket cardinality, including a null bucket for the events the dimension does not apply to, and never a count of entities. To count entities, send `include=distinct` and read `totals.distinct`. `totals` always describes the whole window — never the page above it, and never the bucketing: omit `group_by` and `totals` is unchanged while `groups` is empty.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/usage",httpMethod:"get",pathParams:[],queryParams:["project_id","group_by","include","from","to","meter_type","session_id","actor_id","agent_id","ai_provider_id","orchestration_run_id","orchestration_id","generation_id","trace_id","model","source","trigger_id","action_id","tool_id","publisher_project_id","outcome","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Project public ID to aggregate usage for","required":true,"type":"string","in":"query"},{"name":"group_by","description":"Dimension to bucket by. Omit it to skip bucketing entirely: `totals` still describes the whole window and `groups` is an empty page, which is what \"what did this one thing cost\" wants. A value naming no dimension is a `400`. `day` buckets on the event's UTC calendar day; the others bucket on the matching column. `ai_provider` buckets on the provider the spend was billed against — a routed generation's serving target, or the agent's pinned provider. `model` buckets on the model id *and* the provider that served it, so one model name served by two providers is two groups (see `ai_provider_id`). `source` buckets by what the spend was incurred for (`eval`, `eval_judge`), which is how verification spend is priced apart from the traffic serving real users; unlabelled traffic collapses into the single `null` bucket. `orchestration_run` buckets on the orchestration run the event belongs to — a standalone generation runs no orchestration and collapses into the single `null` bucket, so the bucket count under this dimension is not a count of runs.\n","required":false,"type":"string","in":"query"},{"name":"include","description":"Optional extras to compute. The only value is `distinct`, which adds `totals.distinct` — one distinct-entity count per attribution column. It is opt-in because each key costs the database one extra sort of the window, so the default response keeps its cost whatever the event table grows to carry; a caller counting entities sends one request at `limit=1&include=distinct`. Any other value is a `400`.\n","required":false,"type":"string","in":"query"},{"name":"from","description":"Inclusive lower bound (ISO-8601 timestamp) on the event created_at. Omit for no lower bound.\n","required":false,"type":"string","in":"query"},{"name":"to","description":"Inclusive upper bound (ISO-8601 timestamp) on the event created_at. Omit for no upper bound.\n","required":false,"type":"string","in":"query"},{"name":"meter_type","description":"Narrow the rollup to one meter type (e.g. `llm_tokens`, `compute_execution`, `api_request`, `storage`, `tool_execution`). Omit to include every meter. An unknown type yields an empty rollup, not an error.\n","required":false,"type":"string","in":"query"},{"name":"session_id","description":"Narrow the whole rollup — every bucket, `totals` and `totals.distinct` alike — to one session's traffic. Combines with `group_by`: `group_by=day&session_id=…` is a conversation's spend per day, `group_by=model` is the same spend split by model. Like every id narrowing it matches the id the event recorded, so a deleted session's spend stays selectable; an id no event in this project recorded yields an empty rollup, never the project total.\n","required":false,"type":"string","in":"query"},{"name":"actor_id","description":"The same narrowing, for one end user across every session they appear in. This is the figure behind a `scope: actor`, `metric: cost_usd` quota — what that actor has spent, against the cap it is held to.\n","required":false,"type":"string","in":"query"},{"name":"agent_id","description":"Narrow to one agent's traffic, across every session, run and trigger that dispatched it.\n","required":false,"type":"string","in":"query"},{"name":"ai_provider_id","description":"Narrow to the spend billed against one provider record — a routed generation's serving target, or the agent's pinned provider.\n","required":false,"type":"string","in":"query"},{"name":"orchestration_run_id","description":"Narrow to the events one orchestration run metered.","required":false,"type":"string","in":"query"},{"name":"orchestration_id","description":"Narrow to the runs of one orchestration — the runs it started itself, never the subtree a `loop` or `sub_orchestration` node started under it, which is metered against the child orchestration where it was incurred. Additive: summed across a project's orchestrations the figures reach the project total exactly once. For the subtree figure of a single invocation, read `usage` on the run.\n","required":false,"type":"string","in":"query"},{"name":"generation_id","description":"Narrow to one generation's events. A generation writes more than one when it meters several dimensions (tokens and compute).\n","required":false,"type":"string","in":"query"},{"name":"trace_id","description":"Narrow to the events recorded under one trace.","required":false,"type":"string","in":"query"},{"name":"model","description":"Narrow to one model, matched against the as-billed SKU the event recorded. A value naming no model yields an empty rollup, not an error. Under `group_by=model` this is a single-bucket rollup; under any other dimension it is that model's spend split by the dimension.\n","required":false,"type":"string","in":"query"},{"name":"source","description":"Narrow by what the spend was incurred for — `eval` and `eval_judge` are verification spend, `decider` is decision spend. Ordinary traffic carries no source, so it cannot be selected by this filter; omit the filter for everything.\n","required":false,"type":"string","in":"query"},{"name":"trigger_id","description":"Narrow to the spend one trigger initiated. Matched as recorded rather than resolved, so it still selects the spend of a trigger that has since been deleted.\n","required":false,"type":"string","in":"query"},{"name":"action_id","description":"Narrow to one caller-supplied action label, for per-action spend. Matched as recorded, like `trigger_id`.\n","required":false,"type":"string","in":"query"},{"name":"tool_id","description":"Narrow to the executions of one tool, including one another project shares with this one.\n","required":false,"type":"string","in":"query"},{"name":"publisher_project_id","description":"Narrow to the calls made through a share of a resource the named project owns.\n","required":false,"type":"string","in":"query"},{"name":"outcome","description":"Narrow `tool_execution` events by how the call ended — `ok`, `error` or `timeout` — matched as recorded. `outcome=error` is a tool's failures without a second meter.\n","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"list-usage-thresholds":{serviceClass:"Usage",operationId:"listUsageThresholds",description:"Lists the usage alert thresholds the caller can access, optionally filtered by project_id. Each threshold fires the `usage.threshold_crossed` webhook when a project's cost or token usage over a calendar-month or rolling-24h window crosses the configured value.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/usage",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Filter by project public ID","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-usage-threshold":{serviceClass:"Usage",operationId:"createUsageThreshold",description:"Creates a usage alert threshold on a project. Thresholds are immutable apart from deletion — to change one, delete and recreate it (which resets its fire state).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/usage",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"","required":true,"type":"string","in":"body"},{"name":"metric","description":"","required":true,"type":"string","in":"body"},{"name":"window","description":"","required":true,"type":"string","in":"body"},{"name":"threshold","description":"Must be greater than 0","required":true,"type":"number","in":"body"}]},"delete-usage-threshold":{serviceClass:"Usage",operationId:"deleteUsageThreshold",description:"Deletes a usage threshold, resetting its fire state. Recreating a threshold starts its once-per-window / hysteresis state fresh.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/usage",httpMethod:"delete",pathParams:["threshold_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"threshold_id","description":"Threshold public ID","required":true,"type":"string","in":"path"}]},"get-usage-receipt":{serviceClass:"Usage",operationId:"getUsageReceipt",description:"Returns a billing receipt. Pass generation_id for a per-generation receipt, or orchestration_run_id for a receipt summed across the orchestration run's events — both share the same shape (per-model line items with tokens, the price-book version that priced them, and cost, plus totals). Exactly one of generation_id or orchestration_run_id must be supplied.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/usage",httpMethod:"get",pathParams:[],queryParams:["generation_id","orchestration_run_id"],headerParams:[],cookieParams:[],flags:[{"name":"generation_id","description":"Generation public ID. Mutually exclusive with orchestration_run_id.\n","required":false,"type":"string","in":"query"},{"name":"orchestration_run_id","description":"Orchestration run public ID. Returns the receipt summed across every generation the run metered. Mutually exclusive with generation_id.\n","required":false,"type":"string","in":"query"}]},"get-price-book":{serviceClass:"Usage",operationId:"getPriceBook",description:"Returns the global price book — the versioned per-provider/model unit prices used to compute usage cost at write time. Readable by any authenticated user.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/usage",httpMethod:"get",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[]},"upsert-price-book":{serviceClass:"Usage",operationId:"upsertPriceBook",description:"Upserts price rows keyed on (provider, model, effective_from). Admin only. `effective_from` must be in the future once the (provider, model, component) is priced — past prices are immutable so recorded costs stay explainable; ship corrections as new future-dated rows. A first price, with no row in this scope or any broader one, may be dated now or earlier.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/usage",httpMethod:"put",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"prices","description":"","required":true,"type":"array","in":"body"}]},"get-current-user":{serviceClass:"Users",operationId:"getCurrentUser",description:"Returns the profile of the currently authenticated user",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/users",httpMethod:"get",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[]},"list-users":{serviceClass:"Users",operationId:"listUsers",description:"Returns a list of all users",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/users",httpMethod:"get",pathParams:[],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-user":{serviceClass:"Users",operationId:"createUser",description:"Creates a new user in the system",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/users",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"username","description":"","required":true,"type":"string","in":"body"},{"name":"password","description":"","required":true,"type":"string","in":"body"},{"name":"role","description":"","required":false,"type":"string","in":"body"}]},"get-user":{serviceClass:"Users",operationId:"getUser",description:"Returns the data of a specific user",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/users",httpMethod:"get",pathParams:["user_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"user_id","description":"User ID","required":true,"type":"string","in":"path"}]},"delete-user":{serviceClass:"Users",operationId:"deleteUser",description:"Deletes a specific user",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/users",httpMethod:"delete",pathParams:["user_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"user_id","description":"User ID","required":true,"type":"string","in":"path"}]},"bootstrap-user":{serviceClass:"Users",operationId:"bootstrapUser",description:"Creates the first admin user. Returns 409 if any user already exists.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/users",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"username","description":"","required":true,"type":"string","in":"body"},{"name":"password","description":"","required":true,"type":"string","in":"body"}]},"login-user":{serviceClass:"Users",operationId:"loginUser",description:"Authenticates a user and returns a JWT token",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/users",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"username","description":"","required":true,"type":"string","in":"body"},{"name":"password","description":"","required":true,"type":"string","in":"body"}]},"attach-user-policies":{serviceClass:"Users",operationId:"attachUserPolicies",description:"Replaces the user's policy list with the provided policy IDs. Requires admin role.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/users",httpMethod:"put",pathParams:["user_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"user_id","description":"User public ID (user_ prefix)","required":true,"type":"string","in":"path"},{"name":"policy_ids","description":"List of policy public IDs to attach (replaces existing)","required":true,"type":"array","in":"body"}]},"list-webhooks":{serviceClass:"Webhooks",operationId:"listWebhooks",description:"Lists webhooks. Use the project_id query parameter to filter by project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-webhook":{serviceClass:"Webhooks",operationId:"createWebhook",description:"Creates a new webhook for a project",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"Public ID of the project. Optional when authenticating with a project-scoped API key, which defaults to the key's project; required otherwise.","required":false,"type":"string","in":"body"},{"name":"name","description":"","required":true,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"url","description":"","required":true,"type":"string","in":"body"},{"name":"events","description":"","required":true,"type":"array","in":"body"},{"name":"policy_id","description":"","required":false,"type":"string","in":"body"}]},"get-webhook":{serviceClass:"Webhooks",operationId:"getWebhook",description:"Retrieves the details of a specific webhook",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"get",pathParams:["webhook_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"webhook_id","description":"","required":true,"type":"string","in":"path"}]},"update-webhook":{serviceClass:"Webhooks",operationId:"updateWebhook",description:"Updates an existing webhook's configuration",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"put",pathParams:["webhook_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"webhook_id","description":"","required":true,"type":"string","in":"path"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"url","description":"","required":false,"type":"string","in":"body"},{"name":"events","description":"","required":false,"type":"array","in":"body"},{"name":"active","description":"","required":false,"type":"boolean","in":"body"},{"name":"policy_id","description":"","required":false,"type":"string","in":"body"}]},"delete-webhook":{serviceClass:"Webhooks",operationId:"deleteWebhook",description:"Deletes a webhook and stops all event deliveries",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"delete",pathParams:["webhook_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"webhook_id","description":"","required":true,"type":"string","in":"path"}]},"list-webhook-deliveries":{serviceClass:"Webhooks",operationId:"listWebhookDeliveries",description:"Lists event deliveries for a webhook (webhook_id is required).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"get",pathParams:[],queryParams:["webhook_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"webhook_id","description":"Webhook to list deliveries for (wh_...)","required":true,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-webhook-delivery":{serviceClass:"Webhooks",operationId:"getWebhookDelivery",description:"Retrieves the details of a specific webhook delivery",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"get",pathParams:["delivery_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"delivery_id","description":"","required":true,"type":"string","in":"path"}]},"redeliver-webhook-delivery":{serviceClass:"Webhooks",operationId:"redeliverWebhookDelivery",description:"Queues the stored payload of an existing delivery to be sent again. A new delivery record is created rather than the original being reset, so the original attempt stays in the history. The send happens in the background: poll the returned delivery to observe its outcome.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"post",pathParams:["delivery_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"delivery_id","description":"Delivery to send again (wh_deliv_...)","required":true,"type":"string","in":"path"}]},"get-webhook-secret":{serviceClass:"Webhooks",operationId:"getWebhookSecret",description:"Retrieves the signing secret for the specified webhook",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"get",pathParams:["webhook_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"webhook_id","description":"","required":true,"type":"string","in":"path"}]},"rotate-webhook-secret":{serviceClass:"Webhooks",operationId:"rotateWebhookSecret",description:"Rotates the secret key for the specified webhook",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/webhooks",httpMethod:"post",pathParams:["webhook_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"webhook_id","description":"","required":true,"type":"string","in":"path"}]},"list-workflows":{serviceClass:"Workflows",operationId:"listWorkflows",description:"Lists workflow definitions in a project.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/workflows",httpMethod:"get",pathParams:[],queryParams:["project_id","limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"","required":false,"type":"string","in":"query"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"create-workflow":{serviceClass:"Workflows",operationId:"createWorkflow",description:"Creates a new workflow definition. The definition is statically validated.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/workflows",httpMethod:"post",pathParams:[],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"project_id","description":"","required":false,"type":"string","in":"body"},{"name":"name","description":"","required":true,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"states","description":"","required":true,"type":"array","in":"body"},{"name":"transitions","description":"","required":true,"type":"array","in":"body"},{"name":"payload_schema","description":"","required":false,"type":"object","in":"body"},{"name":"version_label","description":"Optional tag for the version this create archives, e.g. `initial`.","required":false,"type":"string","in":"body"}]},"get-workflow":{serviceClass:"Workflows",operationId:"getWorkflow",description:"Retrieves a workflow definition.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/workflows",httpMethod:"get",pathParams:["workflow_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"workflow_id","description":"","required":true,"type":"string","in":"path"}]},"update-workflow":{serviceClass:"Workflows",operationId:"updateWorkflow",description:"Updates a workflow definition. Structural changes (states/transitions) are re-validated. Existing tasks in a removed state stay put but can only leave via transitions valid in the new definition.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/workflows",httpMethod:"patch",pathParams:["workflow_id"],queryParams:[],headerParams:["If-Match"],cookieParams:[],flags:[{"name":"workflow_id","description":"","required":true,"type":"string","in":"path"},{"name":"If-Match","description":"The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.","required":false,"type":"string","in":"header"},{"name":"name","description":"","required":false,"type":"string","in":"body"},{"name":"description","description":"","required":false,"type":"string","in":"body"},{"name":"states","description":"","required":false,"type":"array","in":"body"},{"name":"transitions","description":"","required":false,"type":"array","in":"body"},{"name":"payload_schema","description":"","required":false,"type":"object","in":"body"},{"name":"version_label","description":"Optional tag for the version this write archives, e.g. `pre-rewire`. Ignored when the write changes no definition field, since no version is archived.","required":false,"type":"string","in":"body"},{"name":"expected_version","description":"Refuses the write unless the resource is at this version.","required":false,"type":"integer","in":"body"}]},"delete-workflow":{serviceClass:"Workflows",operationId:"deleteWorkflow",description:"Deletes a workflow. Rejected while open tasks exist.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/workflows",httpMethod:"delete",pathParams:["workflow_id"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"workflow_id","description":"","required":true,"type":"string","in":"path"}]},"list-workflow-versions":{serviceClass:"Workflows",operationId:"listWorkflowVersions",description:"Returns the workflow's archived state machines, newest first. A version is written on create and on every subsequent write that changes the definition (`states`, `transitions`, `payload_schema`) — through the REST API or a formation apply alike. Metadata-only edits (name, description) do not archive a version. See [Versioning](/docs/modules/workflows#versioning).",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/workflows",httpMethod:"get",pathParams:["workflow_id"],queryParams:["limit","offset"],headerParams:[],cookieParams:[],flags:[{"name":"workflow_id","description":"","required":true,"type":"string","in":"path"},{"name":"limit","description":"Results per page. Above the maximum it is clamped, and `limit` in the response reports the page served; below 1 or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"},{"name":"offset","description":"Results to skip. Negative or not an integer it is `400 VALIDATION_FAILED`.","required":false,"type":"integer","in":"query"}]},"get-workflow-version":{serviceClass:"Workflows",operationId:"getWorkflowVersion",description:"Returns the exact state machine a given version describes. Every task records the version it entered on in `workflow_version` and runs on that machine for its whole life, so this is how you read the definition a task is actually being validated against — including a task whose workflow has been rewired since.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/workflows",httpMethod:"get",pathParams:["workflow_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"workflow_id","description":"","required":true,"type":"string","in":"path"},{"name":"version","description":"The archived version number","required":true,"type":"integer","in":"path"}]},"restore-workflow-version":{serviceClass:"Workflows",operationId:"restoreWorkflowVersion",description:"Writes an archived version's state machine back as the workflow's live definition, which archives it again as a **new** version rather than rewinding the counter — so a task pinned to any version in between still runs on the machine it entered on. The restore runs through the ordinary update path, so the archived definition goes through the same validation as an authored one. That includes resolving every `on_enter` dispatch target, so restoring a version whose agent or orchestration has since been deleted fails with `400` rather than writing a definition that would strand a task on entry. Restoring the definition the workflow already holds is a no-op and archives nothing. Tasks already in flight are unaffected either way — a restore is an ordinary edit, and pinning is what keeps it from reaching them.",moduleDocsUrl:"https://soat.ttoss.dev/docs/modules/workflows",httpMethod:"post",pathParams:["workflow_id","version"],queryParams:[],headerParams:[],cookieParams:[],flags:[{"name":"workflow_id","description":"","required":true,"type":"string","in":"path"},{"name":"version","description":"The archived version number","required":true,"type":"integer","in":"path"},{"name":"label","description":"Optional tag for the new version the restore archives. Defaults to `restored from vN`.","required":false,"type":"string","in":"body"}]}};//#endregion
|
|
56
56
|
//#region src/naming.ts
|
|
57
57
|
/**
|
|
58
58
|
* Flag-name spellings. The CLI accepts kebab-case (the documented convention),
|