openapi: 3.0.3
info:
  title: Agents API
  version: 1.0.0
  description: >
    AI Agents with tool-use capabilities. Create agents bound to AI providers,
    attach tools, run multi-step generations, and inspect execution traces.
  contact:
    name: SOAT API Support

servers:
  - url: '{baseUrl}'
    description: Base URL of your SOAT deployment (e.g. https://your-soat.com or http://localhost:5047)
    variables:
      baseUrl:
        description: The base URL of your SOAT deployment
        default: http://localhost:5047

tags:
  - name: Agents
    description: Manage AI agents
  - name: Agent Versions
    description: Agent config history and staged rollout
  - name: Agent Traces
    description: View agent traces
security:
  - bearerAuth: []

paths:
  /api/v1/agents:
    post:
      tags:
        - Agents
      summary: Create an agent
      description: Creates a new agent bound to an AI provider.
      operationId: createAgent
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAgentRequest'
            examples:
              minimal:
                summary: Minimal agent
                value:
                  ai_provider_id: aip_V1StGXR8Z5jdHi6B
              full:
                summary: Agent with tools and instructions
                value:
                  ai_provider_id: aip_V1StGXR8Z5jdHi6B
                  name: Research Assistant
                  instructions: You are a helpful research assistant.
                  model: gpt-4o
                  tool_bindings:
                    - tool_id: tool_abc123
                  max_steps: 10
                  temperature: 0.7
      responses:
        '201':
          description: Agent created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: AI provider not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    get:
      tags:
        - Agents
      summary: List agents
      description: Returns all agents in the project.
      operationId: listAgents
      parameters:
        - name: project_id
          in: query
          required: false
          schema:
            type: string
          description: Project public ID to filter by
        - name: limit
          in: query
          required: false
          description: Maximum number of results to return
          schema:
            type: integer
            default: 50
        - name: offset
          in: query
          required: false
          description: Number of results to skip
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: List of agents
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - total
                  - limit
                  - offset
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Agent'
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/agents/{agent_id}:
    get:
      tags:
        - Agents
      summary: Get an agent
      description: Returns a single agent by ID.
      operationId: getAgent
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Agent details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    put:
      tags:
        - Agents
      summary: Update an agent
      description: Updates an existing agent. Identical to PATCH — both perform partial updates.
      operationId: updateAgent
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateAgentRequest'
      responses:
        '200':
          description: Agent updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    patch:
      tags:
        - Agents
      summary: Partially update an agent
      description: Partially updates an existing agent. Identical to PUT — both perform partial updates.
      operationId: patchAgent
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateAgentRequest'
      responses:
        '200':
          description: Agent updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - Agents
      summary: Delete an agent
      description: >
        Deletes an agent by ID. Fails with `409` if the agent has dependent
        generations or traces, unless `force=true` is passed, in which case
        those generations and traces are deleted along with the agent.
      operationId: deleteAgent
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
        - name: force
          in: query
          required: false
          description: >
            When `true`, deletes the agent's dependent generations and traces
            instead of returning `409 AGENT_HAS_DEPENDENTS`.
          schema:
            type: boolean
            default: false
      responses:
        '204':
          description: Deleted
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: >
            Agent has dependent generations or traces (pass `force=true` to
            delete anyway). `error.meta` carries `generation_count` and
            `trace_count` so a caller can tell which one is nonzero.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/agents/{agent_id}/generate:
    post:
      tags:
        - Agents
      summary: Run an agent generation
      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.
      operationId: createAgentGeneration
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
        - name: wait
          in: query
          required: false
          # Pinned to `true` for tool calls: a nested agent-to-agent call has no
          # channel to poll a background generation later, so it must block —
          # the mirror image of `stream`, which a tool call cannot receive.
          x-soat-tool-forced: 'true'
          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 `builtin` tool call always waits."
          schema:
            type: boolean
            default: false
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAgentGenerationRequest'
            examples:
              basic:
                summary: Simple generation
                value:
                  messages:
                    - role: user
                      content: What is the weather in Tokyo?
              toolOutput:
                summary: Use a tool output as user message content
                value:
                  messages:
                    - role: user
                      content:
                        type: tool_output
                        tool_id: tool_audio_to_text
                        input:
                          url: https://example.com/audio.mp3
                        output_path: text
              streaming:
                summary: Streaming generation
                value:
                  messages:
                    - role: user
                      content: Summarize the latest report.
                  stream: true
      responses:
        '200':
          description: "Generation result or SSE stream (only when `?wait=true` or `stream: true`)"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentGenerationResponse'
            text/event-stream:
              schema:
                type: string
                description: >
                  SSE stream of delta chunks ending with `data: [DONE]`.


                  The response headers are written before the provider is
                  called, so a failure cannot become a status code once the
                  stream is open. It arrives instead as a terminal
                  `data: {"error": "..."}` frame carrying the same mapped
                  message the non-streaming path returns in its `502` body
                  (e.g. `Provider returned 404: ...`), and the stream then ends
                  **without** a `[DONE]` — the absence of that sentinel is how a
                  caller tells a truncated answer from a complete one. Chunks
                  produced before the failure are still delivered, and the
                  generation is recorded as `failed`.
        '202':
          description: Generation accepted and running in the background (default, when `wait` is omitted or `false`). Poll `GET /api/v1/generations/{generation_id}` for the result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AcceptedGenerationResponse'
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Agent or AI provider not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: >
            Upstream AI provider error (AI_PROVIDER_ERROR); model output that
            does not satisfy the agent's `output_schema`
            (OUTPUT_SCHEMA_VALIDATION_FAILED — the violated field is named in
            the message); or a model that wrote a tool invocation out as plain
            assistant text instead of calling the tool, so the tool never ran
            (TEXT_ENCODED_TOOL_CALL — `meta.tool_name` names the tool). The
            error `meta` includes the `generation_id` and
            `trace_id` of the failed
            generation for post-mortem debugging via GET /api/v1/generations/{generation_id}.
            Streaming requests report the provider error in a terminal SSE frame
            instead, since their status line is already on the wire.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/agents/{agent_id}/generate/{generation_id}/tool-outputs:
    post:
      tags:
        - Agents
      summary: Submit tool outputs for a paused generation
      description: >
        Resumes a generation that was paused due to client tool calls.
        Provide tool outputs for each pending tool call.
      operationId: submitAgentToolOutputs
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
        - name: generation_id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SubmitToolOutputsRequest'
      responses:
        '200':
          description: Generation result after resuming
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentGenerationResponse'
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Agent or generation not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: >
            Upstream AI provider error (AI_PROVIDER_ERROR); model output that
            does not satisfy the agent's `output_schema`
            (OUTPUT_SCHEMA_VALIDATION_FAILED); or a model that wrote a tool
            invocation out as plain assistant text instead of calling the tool
            (TEXT_ENCODED_TOOL_CALL — `meta.tool_name` names the tool). The
            resumed generation is recorded `failed`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/agents/{agent_id}/versions:
    get:
      tags:
        - Agent Versions
      summary: List an agent's config versions
      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).
      operationId: listAgentVersions
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
        - name: limit
          in: query
          required: false
          description: Maximum number of results to return
          schema:
            type: integer
            default: 50
        - name: offset
          in: query
          required: false
          description: Number of results to skip
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: List of agent versions, newest first
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - total
                  - limit
                  - offset
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AgentVersion'
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/agents/{agent_id}/versions/{version}:
    get:
      tags:
        - Agent Versions
      summary: Get an archived agent config version
      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.
      operationId: getAgentVersion
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
        - name: version
          in: path
          required: true
          schema:
            type: integer
            minimum: 1
      responses:
        '200':
          description: Archived agent version
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentVersion'
        '400':
          description: Bad Request — version is not a positive integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Agent or version not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/agents/{agent_id}/versions/{version}/restore:
    post:
      tags:
        - Agent Versions
      summary: Restore an archived config as a new version
      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.
      operationId: restoreAgentVersion
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
        - name: version
          in: path
          required: true
          schema:
            type: integer
            minimum: 1
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RestoreAgentVersionRequest'
      responses:
        '200':
          description: The agent, at its new version
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
        '400':
          description: Bad Request — invalid version, or the archived config no longer validates
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Agent or version not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/agents/{agent_id}/release:
    put:
      tags:
        - Agent Versions
      summary: Set or replace a staged rollout
      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`.
      operationId: setAgentRelease
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetAgentReleaseRequest'
      responses:
        '200':
          description: The agent, with its active release set
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
        '400':
          description: Bad Request — malformed input, or a version that does not exist
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/agents/{agent_id}/release/promote:
    post:
      tags:
        - Agent Versions
      summary: Promote the canary and end the rollout
      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.
      operationId: promoteAgentRelease
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: The agent, now serving the promoted config
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: >-
            Conflict — the agent has no active release
            (`NO_ACTIVE_RELEASE`), or its `promotion_gate` has no passing eval
            run against the canary version (`PROMOTION_GATE_UNMET`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/agents/{agent_id}/release/abort:
    post:
      tags:
        - Agent Versions
      summary: Abort the rollout and roll back to stable
      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.
      operationId: abortAgentRelease
      parameters:
        - name: agent_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: The agent, back on the stable config
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Conflict — the agent has no active release
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: JWT token or sk_ api key
  schemas:
    Agent:
      type: object
      properties:
        id:
          type: string
          description: Public ID of the agent
          example: agent_V1StGXR8Z5jdHi6B
        project_id:
          type: string
          description: Public ID of the owning project
          x-soat-ref: projects
        ai_provider_id:
          type: string
          nullable: true
          description: >-
            Public ID of the pinned AI provider. Null when the agent resolves
            its model through `model_route_id` instead.
          x-soat-ref: ai-providers
        model_route_id:
          type: string
          nullable: true
          description: >-
            Public ID of the model route that resolves this agent's completion
            model. Null when the agent pins a provider through
            `ai_provider_id`. Mutually exclusive with `ai_provider_id` and
            `model`.
          x-soat-ref: model-routes
        name:
          type: string
          nullable: true
          description: Display name
        instructions:
          type: string
          nullable: true
          description: System instructions guiding behavior
        model:
          type: string
          nullable: true
          description: Model identifier
        tool_bindings:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/ToolBinding'
          description: >-
            Tools attached to this agent, one binding object per tool — the
            canonical attachment field. See
            [Tool Bindings](/docs/modules/agents#tool-bindings).
        max_steps:
          type: integer
          nullable: true
          description: >-
            Maximum agent loop steps before stopping. The budget bounds a
            **turn**: a generation that pauses at `requires_action` and resumes
            after `submit-tool-outputs` continues the same turn and spends what
            is left of it, so a turn that arrives with nothing left completes
            with `stop_reason: "max_steps"` instead of calling the model again.
        tool_choice:
          nullable: true
          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`.'
        stop_conditions:
          type: array
          nullable: true
          items:
            type: object
          description: >-
            Conditions that end the agent's work early, on top of `max_steps` —
            turn-scoped (`has_tool_call`) or chain-scoped
            (`max_chain_generations`). See the create request body for the
            accepted shapes.
        active_tool_ids:
          x-soat-ref: tools
          type: array
          nullable: true
          items:
            type: string
          description: Subset of the bound tools that are active
        guardrail_ids:
          x-soat-ref: guardrails
          type: array
          nullable: true
          items:
            type: string
          description: >-
            Guardrails attached at the agent scope, governing every tool call
            the agent makes.
        step_rules:
          type: array
          nullable: true
          items:
            type: object
          description: >-
            Per-step overrides of `tool_choice` and `active_tool_ids`. Steps are
            numbered from the first step of the **turn**, and that numbering
            spans a `requires_action` pause — a rule fires once per turn, not
            once per resumption.
        boundary_policy:
          type: object
          nullable: true
          description: Allowed/denied SOAT actions
        temperature:
          type: number
          nullable: true
          description: Sampling temperature
        knowledge_config:
          type: object
          nullable: true
          description: Knowledge retrieval config injected before every generation
          properties:
            memory_ids:
              x-soat-ref: memories
              type: array
              items:
                type: string
            memory_tags:
              type: array
              items:
                type: string
            document_ids:
              x-soat-ref: documents
              type: array
              items:
                type: string
            document_paths:
              type: array
              items:
                type: string
            min_score:
              type: number
            limit:
              type: integer
            write_memory_id:
              x-soat-ref: memories
              type: string
              nullable: true
              description: Public ID of the memory the agent can write to during generation. When set, a write_memory tool is automatically available to the agent.
            extraction:
              description: Automatic fact extraction from completed generation turns (requires write_memory_id). Pass `true` to enable with defaults, or an object to customize the provider, model, and prompt used for the extraction completion. Extracted facts are written to the write memory through the standard dedup/merge/skip algorithm.
              oneOf:
                - type: boolean
                - type: object
                  properties:
                    enabled:
                      type: boolean
                      description: Defaults to true when the object form is used. Set false to keep the configuration but disable extraction.
                    ai_provider_id:
                      x-soat-ref: ai-providers
                      type: string
                      description: AI provider override for extraction calls. Must belong to the agent's project. Its default_model becomes the model fallback.
                    model:
                      type: string
                      description: Model override for extraction calls.
                    prompt:
                      type: string
                      description: Replaces the default task instructions. The JSON response contract and the conversation transcript are always appended by the server.
        output_schema:
          type: object
          nullable: true
          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.'
        max_context_messages:
          type: integer
          nullable: true
          description: Maximum number of recent messages to include in the context window sent to the model. When null, all messages are included.
        single_session_per_actor:
          type: boolean
          description: When true, only one open session per actor_id is allowed for this agent. Creating a second open session for the same actor returns 409.
        trace_content_mode:
          type: string
          nullable: true
          # `null` is a real value here (inherit the project), so it belongs in
          # the enum — otherwise the generated SDK type omits it (#861).
          enum: [full, none, null]
          description: >-
            Agent-scope zero-retention setting. `null` (the default) inherits
            the project's `trace_content_mode`; `none` means this agent's trace
            and generation content is never persisted. An agent may tighten a
            storing project to `none` but cannot loosen a `none` project back
            to `full`.
        on_approval_expiry:
          type: string
          nullable: true
          # `null` is a real value here (the terminating default), so it belongs
          # in the enum — otherwise the generated SDK type omits it (#861).
          enum: [terminate, react, null]
          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.
        version:
          type: integer
          description: >-
            Current config version. Starts at 1 and increments on every write
            that changes the config; each increment archives the new config as an
            `AgentVersion`. A write that changes nothing leaves it untouched.
          example: 3
        active_release:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/AgentRelease'
          description: >-
            Staged rollout in progress, or null when all traffic serves this
            config.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    AgentRelease:
      type: object
      description: >-
        A staged rollout splitting traffic between two archived versions. See
        [Versioning and Staged Rollout](/docs/modules/agents#versioning-and-staged-rollout).
      required:
        - stable_version
        - canary_version
        - canary_percent
      properties:
        stable_version:
          type: integer
          minimum: 1
          description: Version served to traffic not assigned to the canary
          example: 3
        canary_version:
          type: integer
          minimum: 1
          description: Version under trial. Must differ from `stable_version`.
          example: 4
        canary_percent:
          type: integer
          minimum: 0
          maximum: 100
          description: Percentage of traffic assigned to `canary_version`
          example: 20
        promotion_gate:
          x-soat-ref: evals
          type: string
          nullable: true
          description: >-
            Eval that must have a passing run against `canary_version` before
            `promote` is allowed, or null for an ungated rollout. The gate
            constrains only how the rollout ends — traffic is split the same way
            either way. See
            [Eval-gated promotion](/docs/modules/agents#eval-gated-promotion).
          example: eval_V1StGXR8Z5jdHi6B

    AgentVersion:
      type: object
      description: >-
        An immutable archive of an agent's configuration at one version.
      properties:
        id:
          type: string
          description: Public ID of the archived version
          example: agver_V1StGXR8Z5jdHi6B
        agent_id:
          x-soat-ref: agents
          type: string
          description: Public ID of the agent this version belongs to
          example: agent_V1StGXR8Z5jdHi6B
        version:
          type: integer
          description: The archived version number
          example: 1
        config:
          type: object
          additionalProperties: true
          description: >-
            The agent's configuration as it stood at this version: every mutable
            field of the `Agent` schema (`instructions`, `model`,
            `tool_bindings`, `max_steps`, `tool_choice`, `stop_conditions`,
            `active_tool_ids`, `step_rules`, `boundary_policy`, `temperature`,
            `knowledge_config`, `output_schema`, `max_context_messages`,
            `single_session_per_actor`, `on_approval_expiry`,
            `guardrail_ids`, `ai_provider_id`,
            `model_route_id`, `name`), and none of its identity or bookkeeping
            fields (`id`, `project_id`, `version`, `active_release`, timestamps).


            Deliberately open rather than a fixed schema: an archive written by
            an earlier release of SOAT reflects the agent surface **of its own
            time**, so it may carry fields the current schema no longer defines,
            or lack ones it has since gained. Knowledge retrieval is not part of
            the snapshot — a version records which `knowledge_config` applied,
            while the documents and memories it resolves keep their own
            histories and are pinned at generation time.
        label:
          type: string
          nullable: true
          description: >-
            Optional human tag, set with `version_label` on the write that
            created this version. Restore, promote and abort set one
            automatically (e.g. `restored from v1`).
          example: pre-tone-change
        eval_run_id:
          x-soat-ref: eval-runs
          type: string
          nullable: true
          description: >-
            The eval run that cleared the release's `promotion_gate` when this
            version was promoted. Null for every version that did not go live
            through a gated promotion — which is most of them.
          example: evrun_V1StGXR8Z5jdHi6B
        created_by:
          x-soat-ref: users
          type: string
          nullable: true
          description: >-
            Public ID of the user whose action produced this version. A formation
            apply is attributed to the project's owning identity; null when no
            principal could be resolved.
        created_at:
          type: string
          format: date-time

    RestoreAgentVersionRequest:
      type: object
      properties:
        label:
          type: string
          description: >-
            Tag for the version this restore creates. Defaults to
            `restored from v{version}`.
          example: rollback-incident-42

    SetAgentReleaseRequest:
      type: object
      required:
        - stable_version
        - canary_version
        - canary_percent
      properties:
        stable_version:
          type: integer
          minimum: 1
          description: An existing version to serve as the baseline
          example: 3
        canary_version:
          type: integer
          minimum: 1
          description: An existing version to trial. Must differ from `stable_version`.
          example: 4
        canary_percent:
          type: integer
          minimum: 0
          maximum: 100
          description: Percentage of traffic to assign to `canary_version`
          example: 20
        promotion_gate:
          x-soat-ref: evals
          type: string
          nullable: true
          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.
          example: eval_V1StGXR8Z5jdHi6B

    ToolBinding:
      type: object
      description: >-
        One agent↔tool attachment. Exactly one of `tool_id` (persisted tool
        reference) or `tool` (inline ephemeral definition) per entry. Tool-call
        gating is owned by [Guardrails](/docs/modules/guardrails), attached via
        `guardrail_ids` on the project, agent, or tool — not on the binding.
      properties:
        tool_id:
          x-soat-ref: tools
          type: string
          description: Public ID of a persisted tool. Exactly one of `tool_id`/`tool`.
          example: tool_V1StGXR8Z5jdHi6B
        tool:
          # Inline (ephemeral) tool definition — resolved fresh at generation
          # time, never persisted as a Tool resource. Exactly one of
          # `tool_id`/`tool`. Cannot be of type `pipeline`.
          $ref: './tools.yaml#/components/schemas/CreateToolRequest'

    CreateAgentRequest:
      type: object
      description: >-
        Exactly one of `ai_provider_id` or `model_route_id` must be set (400
        otherwise). `model` names the model on a pinned provider and cannot be
        combined with `model_route_id`, whose targets each name their own model.
      properties:
        project_id:
          x-soat-ref: projects
          type: string
          description: Public ID of the project
        ai_provider_id:
          x-soat-ref: ai-providers
          type: string
          description: >-
            Public ID of the AI provider to pin. Mutually exclusive with
            `model_route_id`.
        model_route_id:
          x-soat-ref: model-routes
          type: string
          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`.
        name:
          type: string
        instructions:
          type: string
        model:
          type: string
        tool_bindings:
          type: array
          items:
            $ref: '#/components/schemas/ToolBinding'
          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).
        max_steps:
          type: integer
        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`.'
        stop_conditions:
          type: array
          items:
            type: object
          description: >-
            Conditions that end the agent's work early, on top of `max_steps`.
            Two scopes:


            `{"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`.


            `{"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.


            An 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.
        active_tool_ids:
          x-soat-ref: tools
          type: array
          items:
            type: string
        guardrail_ids:
          x-soat-ref: guardrails
          type: array
          nullable: true
          items:
            type: string
          description: Guardrails attached at the agent scope.
        step_rules:
          type: array
          items:
            type: object
        boundary_policy:
          type: object
        temperature:
          type: number
        knowledge_config:
          type: object
          properties:
            memory_ids:
              x-soat-ref: memories
              type: array
              items:
                type: string
            memory_tags:
              type: array
              items:
                type: string
            document_ids:
              x-soat-ref: documents
              type: array
              items:
                type: string
            document_paths:
              type: array
              items:
                type: string
            min_score:
              type: number
            limit:
              type: integer
            write_memory_id:
              x-soat-ref: memories
              type: string
              nullable: true
              description: Public ID of the memory the agent can write to during generation. When set, a write_memory tool is automatically available to the agent.
            extraction:
              description: Automatic fact extraction from completed generation turns (requires write_memory_id). Pass `true` to enable with defaults, or an object to customize the provider, model, and prompt used for the extraction completion. Extracted facts are written to the write memory through the standard dedup/merge/skip algorithm.
              oneOf:
                - type: boolean
                - type: object
                  properties:
                    enabled:
                      type: boolean
                      description: Defaults to true when the object form is used. Set false to keep the configuration but disable extraction.
                    ai_provider_id:
                      x-soat-ref: ai-providers
                      type: string
                      description: AI provider override for extraction calls. Must belong to the agent's project. Its default_model becomes the model fallback.
                    model:
                      type: string
                      description: Model override for extraction calls.
                    prompt:
                      type: string
                      description: Replaces the default task instructions. The JSON response contract and the conversation transcript are always appended by the server.
        output_schema:
          type: object
          nullable: true
          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.'
        max_context_messages:
          type: integer
          description: Maximum number of recent messages included in the context window. Null means no limit.
        single_session_per_actor:
          type: boolean
          description: When true, only one open session per actor_id is allowed for this agent.
        trace_content_mode:
          type: string
          nullable: true
          # `null` is a real value here (inherit the project), so it belongs in
          # the enum — otherwise the generated SDK type omits it (#861).
          enum: [full, none, null]
          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.
        on_approval_expiry:
          type: string
          nullable: true
          # `null` is a real value here (the terminating default), so it belongs
          # in the enum — otherwise the generated SDK type omits it (#861).
          enum: [terminate, react, null]
          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.
        version_label:
          type: string
          nullable: true
          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.
          example: initial

    UpdateAgentRequest:
      type: object
      description: >-
        The post-update state must still set exactly one of `ai_provider_id` or
        `model_route_id`. To switch a pinned agent to a route, send
        `model_route_id` together with `ai_provider_id: null` in the same
        request (and vice versa).
      properties:
        ai_provider_id:
          x-soat-ref: ai-providers
          type: string
          nullable: true
        model_route_id:
          x-soat-ref: model-routes
          type: string
          nullable: true
          description: >-
            Model route in the same project. Mutually exclusive with
            `ai_provider_id` and `model`; set to null to clear.
        name:
          type: string
          nullable: true
        instructions:
          type: string
          nullable: true
        model:
          type: string
          nullable: true
        tool_bindings:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/ToolBinding'
          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).
        max_steps:
          type: integer
          nullable: true
        tool_choice:
          nullable: true
          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`.'
        stop_conditions:
          type: array
          nullable: true
          items:
            type: object
          description: >-
            Conditions that end the agent's work early, on top of `max_steps`.
            Two scopes:


            `{"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`.


            `{"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.


            An 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.
        active_tool_ids:
          x-soat-ref: tools
          type: array
          nullable: true
          items:
            type: string
        guardrail_ids:
          x-soat-ref: guardrails
          type: array
          nullable: true
          items:
            type: string
          description: Guardrails attached at the agent scope.
        step_rules:
          type: array
          nullable: true
          items:
            type: object
        boundary_policy:
          type: object
          nullable: true
        temperature:
          type: number
          nullable: true
        knowledge_config:
          type: object
          nullable: true
          properties:
            memory_ids:
              x-soat-ref: memories
              type: array
              items:
                type: string
            memory_tags:
              type: array
              items:
                type: string
            document_ids:
              x-soat-ref: documents
              type: array
              items:
                type: string
            document_paths:
              type: array
              items:
                type: string
            min_score:
              type: number
            limit:
              type: integer
            write_memory_id:
              x-soat-ref: memories
              type: string
              nullable: true
              description: Public ID of the memory the agent can write to during generation. When set, a write_memory tool is automatically available to the agent.
            extraction:
              description: Automatic fact extraction from completed generation turns (requires write_memory_id). Pass `true` to enable with defaults, or an object to customize the provider, model, and prompt used for the extraction completion. Extracted facts are written to the write memory through the standard dedup/merge/skip algorithm.
              oneOf:
                - type: boolean
                - type: object
                  properties:
                    enabled:
                      type: boolean
                      description: Defaults to true when the object form is used. Set false to keep the configuration but disable extraction.
                    ai_provider_id:
                      x-soat-ref: ai-providers
                      type: string
                      description: AI provider override for extraction calls. Must belong to the agent's project. Its default_model becomes the model fallback.
                    model:
                      type: string
                      description: Model override for extraction calls.
                    prompt:
                      type: string
                      description: Replaces the default task instructions. The JSON response contract and the conversation transcript are always appended by the server.
        output_schema:
          type: object
          nullable: true
          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.'
        max_context_messages:
          type: integer
          nullable: true
          description: Maximum number of recent messages included in the context window. Null means no limit.
        single_session_per_actor:
          type: boolean
          nullable: true
          description: When true, only one open session per actor_id is allowed for this agent.
        trace_content_mode:
          type: string
          nullable: true
          # `null` is a real value here (inherit the project), so it belongs in
          # the enum — otherwise the generated SDK type omits it (#861).
          enum: [full, none, null]
          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.
        on_approval_expiry:
          type: string
          nullable: true
          # `null` is a real value here (the terminating default), so it belongs
          # in the enum — otherwise the generated SDK type omits it (#861).
          enum: [terminate, react, null]
          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.
        version_label:
          type: string
          nullable: true
          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.
          example: pre-tone-change

    CreateAgentGenerationRequest:
      type: object
      required:
        - messages
      properties:
        messages:
          type: array
          minItems: 1
          items:
            type: object
            additionalProperties: false
            required:
              - role
              - content
            properties:
              role:
                type: string
                # No `system`: an agent's system prompt is its `instructions`
                # field; a system entry here is refused with
                # 400 SYSTEM_MESSAGE_NOT_ALLOWED.
                enum:
                  - user
                  - assistant
              content:
                oneOf:
                  - type: string
                  - $ref: '#/components/schemas/ToolOutputMessageContent'
                  - $ref: '#/components/schemas/DocumentMessageContent'
        stream:
          type: boolean
          default: false
          # A tool call is one request and one result; there is no channel to
          # stream deltas over, and the route answers a streamed request by
          # writing SSE frames straight to the socket instead of setting a body
          # — which in-process would run the generation and then have nowhere to
          # put it. Hiding the field means a caller cannot ask for a mode that
          # cannot be delivered; the same action without it returns the
          # completed generation.
          x-soat-tool-unsupported: true
          description: When true the response is an SSE stream
        trace_id:
          x-soat-ref: traces
          type: string
          x-soat-server-managed: true
          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.
        parent_trace_id:
          x-soat-ref: traces
          type: string
          nullable: true
          x-soat-server-managed: true
          description: The trace ID of the parent agent generation that triggered this one (for agent-to-agent calls)
        root_trace_id:
          x-soat-ref: traces
          type: string
          nullable: true
          x-soat-server-managed: true
          description: The trace ID of the root generation in the call chain; if omitted, this generation is the root
        max_call_depth:
          type: integer
          minimum: 0
          default: 10
          x-soat-server-managed: true
          description: Maximum nested agent-call depth; 0 short-circuits with a depth-guard response
        tool_context:
          type: object
          additionalProperties:
            type: string
          nullable: true
          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`.
        action_id:
          type: string
          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).
        guardrail_context:
          type: object
          additionalProperties: true
          nullable: true
          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.
        metadata:
          type: object
          additionalProperties: true
          nullable: true
          description: >-
            Caller-supplied key/value metadata attached to the generation
            record for per-run audit attribution (e.g. the knowledge-corpus
            version that produced this action). 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) 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.
        extract:
          type: boolean
          description: >-
            Per-turn override of the agent's `knowledge_config.extraction`
            default. Omit to follow the agent's stored config. Set `false` to
            suppress automatic memory extraction for this turn (e.g. an
            operational or tool-listing turn that would only add noise to a
            curated memory). Set `true` to force extraction on for this turn
            even when the agent does not enable it by default, provided the
            agent has a `write_memory_id`. Has no effect on streaming or
            `requires_action` turns, which never extract.
        knowledge_config:
          type: object
          nullable: true
          description: Per-generation knowledge retrieval override. Array filters (memory_ids, memory_tags, document_ids, document_paths) are unioned with the agent's stored knowledge_config; scalar fields (min_score, limit) use the per-generation value when present.
          properties:
            memory_ids:
              x-soat-ref: memories
              type: array
              items:
                type: string
            memory_tags:
              type: array
              items:
                type: string
            document_ids:
              x-soat-ref: documents
              type: array
              items:
                type: string
            document_paths:
              type: array
              items:
                type: string
            min_score:
              type: number
            limit:
              type: integer

    ToolOutputMessageContent:
      type: object
      required:
        - type
        - tool_id
      properties:
        type:
          type: string
          enum:
            - tool_output
        tool_id:
          x-soat-ref: tools
          type: string
          description: Public ID of the tool to execute before generation.
        action:
          type: string
          nullable: true
          description: Optional action name for tools that require action selection (for example builtin and mcp tools).
        input:
          type: object
          nullable: true
          additionalProperties: true
          description: Input payload passed to the tool call.
        output_path:
          type: string
          nullable: true
          description: Optional dot-notation path used to extract a value from the tool output.

    DocumentMessageContent:
      type: object
      required:
        - type
        - document_id
      properties:
        type:
          type: string
          enum:
            - document
        document_id:
          x-soat-ref: documents
          type: string
          description: Public ID of a document to use as the message content.

    SubmitToolOutputsRequest:
      type: object
      required:
        - tool_outputs
      properties:
        tool_outputs:
          type: array
          minItems: 1
          items:
            type: object
            required:
              - tool_call_id
              - output
            properties:
              tool_call_id:
                type: string
                description: ID of the tool call to respond to
              output:
                description: Result of the tool execution

    AcceptedGenerationResponse:
      type: object
      description: >
        Handle for a generation running in the background. The generation
        record already exists when this is returned, so the id is immediately
        pollable via `GET /api/v1/generations/{generation_id}`.
      required:
        - status
        - generation_id
        - trace_id
      properties:
        status:
          type: string
          enum: [accepted]
          example: accepted
        generation_id:
          type: string
          example: 'gen_V1StGXR8Z5jdHi6B'
        trace_id:
          type: string
          example: 'trace_V1StGXR8Z5jdHi6B'

    AgentGenerationResponse:
      type: object
      description: >
        Result of an agent generation. Mirrors the server's `GenerationResult`.
        When `status` is `completed` the model output is under `output`; when it
        is `requires_action` the pending client tool calls are under
        `required_action`.
      required:
        - id
        - trace_id
        - status
      properties:
        id:
          type: string
          description: Public ID of the generation
          example: gen_V1StGXR8Z5jdHi6B
        trace_id:
          type: string
          description: Public ID of the trace for this generation
          example: trace_V1StGXR8Z5jdHi6B
        status:
          type: string
          enum:
            - completed
            - requires_action
          description: Generation status
        ai_provider_id:
          type: string
          x-soat-ref: ai-providers
          nullable: true
          description: >
            Public ID of the AI provider that served `output.model` — the target
            a model route picked, or the agent's pinned provider. A model string
            alone does not identify its provider: two providers in one project
            can serve byte-identical model names, so this is what makes the
            value safe to map back to a name a gateway in front of this runtime
            publishes. Null when the generation resolved no serving provider.
          example: aip_V1StGXR8Z5jdHi6B
        output:
          type: object
          nullable: true
          description: Model output (present when `status` is `completed`).
          required:
            - model
            - content
            - finish_reason
          properties:
            model:
              type: string
              description: Model that produced the output
            content:
              type: string
              description: Final text output
            finish_reason:
              type: string
              description: Reason the model stopped generating
            response_messages:
              type: array
              nullable: true
              description: Full AI SDK response messages (tool calls, tool results, final text)
              items:
                type: object
            object:
              type: object
              nullable: true
              description: Structured object matching the agent's `output_schema` (when `output_schema` is set)
        required_action:
          type: object
          nullable: true
          description: Pending action the caller must satisfy (present when `status` is `requires_action`).
          required:
            - type
            - tool_calls
          properties:
            type:
              type: string
              enum:
                - submit_tool_outputs
              description: The kind of action required
            tool_calls:
              type: array
              description: Pending tool calls to execute and submit outputs for
              items:
                type: object
                properties:
                  id:
                    type: string
                    description: Tool call ID
                  tool_name:
                    type: string
                    description: Name of the tool to invoke
                  args:
                    type: object
                    description: Arguments for the tool call

    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          description: >-
            Structured error. Every error response uses this shape — 401, 403
            and the 500 catch-all included — so `code` can be read without
            first checking the type of `error`.
          required:
            - code
            - message
            - hint
            - docs_url
          properties:
            code:
              type: string
              description: A key from the server's ERROR_CODES registry.
              example: RESOURCE_NOT_FOUND
            message:
              type: string
              example: 'Resource not found'
            hint:
              type: string
              description: >-
                What to do about this error. Resolved per code, so a caller that
                has never seen the code before can act on the response without
                leaving it.
              example: >-
                Check the id, and check that the credential can see the project
                that owns the resource.
            docs_url:
              type: string
              format: uri
              description: The reference-page anchor documenting this code.
              example: >-
                https://soat.ttoss.dev/docs/error-codes#resource_not_found
            meta:
              type: object
              description: Optional structured context for the error.
