# AUTO-GENERATED tier-A mirror spec — do not edit.
# Source: openapi/runtime/agents.yaml (runtime 0.71.2)
#  + policy: openapi/mirror-policies/agents.json
# Regenerate: node scripts/generate-mirror-specs.mjs
openapi: 3.0.3
info:
  title: naturali.ai — Agents API
  version: 1.0.0
  description: >-
    Agents: their configuration, tool bindings, versions, releases, and one-shot generations. A
    fully runtime-backed module — this spec is generated verbatim from the runtime's own, re-rooted
    under /v1/projects/{project_id}. The project in the path is authorized by naturali and enforced
    upstream by the project's scoped credential.


    This module mirrors the upstream runtime verbatim (tier A, #304): paths are the runtime's own
    re-rooted under /v1/projects/{project_id}, and every field, method, status code and error shape
    passes through unchanged. Errors raised by the runtime arrive in its envelope; errors raised by
    naturali itself (authentication, project resolution, an unreachable runtime) use naturali's.
  contact:
    name: naturali.ai
    url: https://naturali.ai
servers:
  - url: "{baseUrl}"
    description: Host of your naturali.ai deployment; every path carries the /v1 prefix.
    variables:
      baseUrl:
        description: Base host URL.
        default: https://api.naturali.ai
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: []
  - oauth2:
      - mcp:access
paths:
  /v1/projects/{project_id}/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: limit
          in: query
          required: false
          description: Maximum number of results to return
          schema:
            type: integer
            minimum: 1
            maximum: 100
            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"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/agents/{agent_id}:
    get:
      tags:
        - Agents
      summary: Get an agent
      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.
      operationId: getAgent
      x-naturali-resource:
        kind: agent
        from: agent_id
      parameters:
        - $ref: "#/components/parameters/AgentId"
      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
      x-naturali-resource:
        kind: agent
        from: agent_id
      parameters:
        - $ref: "#/components/parameters/AgentId"
        - $ref: "#/components/parameters/IfMatchVersion"
      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"
        "409":
          $ref: "#/components/responses/VersionConflict"
    patch:
      tags:
        - Agents
      summary: Partially update an agent
      description: Partially updates an existing agent. Identical to PUT — both perform partial updates.
      operationId: patchAgent
      x-naturali-resource:
        kind: agent
        from: agent_id
      parameters:
        - $ref: "#/components/parameters/AgentId"
        - $ref: "#/components/parameters/IfMatchVersion"
      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"
        "409":
          $ref: "#/components/responses/VersionConflict"
    delete:
      tags:
        - Agents
      summary: Delete an agent
      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.
      operationId: deleteAgent
      x-naturali-resource:
        kind: agent
        from: agent_id
      parameters:
        - $ref: "#/components/parameters/AgentId"
        - name: force
          in: query
          required: false
          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`.
          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, traces, accepted shares or ingestion rules in its own
            project (pass `force=true` to delete anyway; records and rules another project made
            through a share are kept). `error.meta` carries `generation_count`, `trace_count`,
            `accepted_share_count` and `ingestion_rule_count` so a caller can tell which one is
            nonzero.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/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
        /v1/projects/{project_id}/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.
      operationId: createAgentGeneration
      x-naturali-resource:
        kind: agent
        from: agent_id
      parameters:
        - $ref: "#/components/parameters/AgentId"
        - name: wait
          in: query
          required: false
          x-naturali-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`. An MCP 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`), or — in every mode, streamed included — a duplicate request: the generation
            the `idempotency_key` already names is returned in whatever state it has reached and
            nothing runs. Poll `GET /v1/projects/{project_id}/generations/{generation_id}` for the
            result."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AcceptedGenerationResponse"
        "400":
          description: Bad Request (e.g. an `idempotency_key` that is not a non-empty string of at most 255
            characters)
          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"
        "409":
          description: "`IDEMPOTENCY_KEY_REUSED` — the key is already claimed by a generation started from a
            different request. Nothing runs."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: "`QUOTA_EXCEEDED`: an `enforce`-mode generation quota is exhausted; `error.meta`
            carries `quota_id`, `metric`, `limit`, `window` and `resets_at`. `SHARE_CAP_EXCEEDED`:
            the agent is another project's, reached through a share whose `cap` is spent for the
            window; `error.meta.retry_after` says when. Both carry a `Retry-After` header."
          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
            /v1/projects/{project_id}/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"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/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
      x-naturali-resource:
        kind: agent
        from: agent_id
      parameters:
        - $ref: "#/components/parameters/AgentId"
        - $ref: "#/components/parameters/GenerationId"
      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"
        "409":
          description: >
            The generation is not paused on client tool calls
            (GENERATION_NOT_AWAITING_TOOL_OUTPUTS): it never paused, or its outputs were already
            submitted. Each pause accepts outputs once.
          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"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/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
      x-naturali-resource:
        kind: agent
        from: agent_id
      parameters:
        - $ref: "#/components/parameters/AgentId"
        - name: limit
          in: query
          required: false
          description: Maximum number of results to return
          schema:
            type: integer
            minimum: 1
            maximum: 100
            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"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/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
      x-naturali-resource:
        kind: agent
        from: agent_id
      parameters:
        - $ref: "#/components/parameters/AgentId"
        - $ref: "#/components/parameters/AgentVersionNumber"
      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"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/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
      x-naturali-resource:
        kind: agent
        from: agent_id
      parameters:
        - $ref: "#/components/parameters/AgentId"
        - $ref: "#/components/parameters/AgentVersionNumber"
      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"
        "409":
          $ref: "#/components/responses/VersionConflict"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/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
      x-naturali-resource:
        kind: agent
        from: agent_id
      parameters:
        - $ref: "#/components/parameters/AgentId"
      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"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/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
      x-naturali-resource:
        kind: agent
        from: agent_id
      parameters:
        - $ref: "#/components/parameters/AgentId"
      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"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/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
      x-naturali-resource:
        kind: agent
        from: agent_id
      parameters:
        - $ref: "#/components/parameters/AgentId"
      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"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
components:
  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-naturali-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-naturali-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-naturali-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:
          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-naturali-ref: tools
          type: array
          nullable: true
          items:
            type: string
          description: Subset of the bound tools that are active
        guardrail_ids:
          x-naturali-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 runtime 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_store_ids:
              x-naturali-ref: memory-stores
              type: array
              items:
                type: string
            document_ids:
              x-naturali-ref: documents
              type: array
              items:
                type: string
            document_paths:
              type: array
              items:
                type: string
            tags:
              description: Key-value pairs a result's own `tags` must all contain (exact match). Scopes documents
                and memories alike.
              allOf:
                - $ref: "#/components/schemas/TagBag"
            min_score:
              type: number
              description: >
                Minimum raw cosine similarity (0–1) a vector candidate must reach to be ranked, the
                floor the search endpoint spells `min_similarity`. No default: omitted means no
                floor at all, and every one of the `limit` nearest chunks is injected however weak
                it is.
            rrf_k:
              type: integer
              description: >
                The `k` in the fusion term `1 / (k + rank)`, the same knob knowledge search takes.
                Smaller weights the top of each ranking more heavily. Omitted, the deployment's
                `KNOWLEDGE_RRF_K` applies.
            recency_half_life_days:
              type: number
              description: >
                Half-life in days of the decay applied to **memory** results after fusion, the same
                knob knowledge search takes. `0` disables it. Omitted, the deployment's
                `KNOWLEDGE_RECENCY_HALF_LIFE_DAYS` applies.
            limit:
              type: integer
              description: |
                Maximum number of results to inject. Omitted, 10 are injected.
            write_memory_store_id:
              x-naturali-ref: memory-stores
              type: string
              nullable: true
              description: Public ID of the memory store the agent can write to during generation. When set, a
                write_memory tool is automatically available to the agent.
        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."
        prompt_caching:
          type: object
          nullable: true
          description: "Prompt caching for this agent's turns. `{\"enabled\": true}` marks a cache breakpoint
            at the end of the turn's static prefix — the tool definitions and the instructions
            together — so a provider that caches by explicit breakpoint reads that prefix back on
            every later step of the turn and every later turn of the session instead of being
            charged for it again. 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. An agent
            with no `instructions` has no block to mark and caches nothing. Cache reads are reported
            as `cached_tokens` and cache writes as `cache_write_tokens` on usage."
          properties:
            enabled:
              type: boolean
              description: Whether the breakpoint is marked. Defaults to false.
        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
          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
          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-naturali-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-naturali-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 the runtime 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 memory stores 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-naturali-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-naturali-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
      additionalProperties: false
      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
      additionalProperties: false
      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-naturali-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-naturali-ref: tools
          type: string
          description: Public ID of a persisted tool in the agent's own project (`400 TOOL_NOT_FOUND`
            otherwise). Exactly one of `tool_id`/`tool`.
          example: tool_V1StGXR8Z5jdHi6B
        tool:
          $ref: "#/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.
      additionalProperties: false
      properties:
        ai_provider_id:
          x-naturali-ref: ai-providers
          type: string
          description: Public ID of the AI provider to pin. Mutually exclusive with `model_route_id`.
        model_route_id:
          x-naturali-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:
          description: Display name.
          type: string
        instructions:
          description: System instructions, sent as the system message of every generation.
          type: string
        model:
          description: Model identifier on the pinned AI provider. Omitted, the provider's default model is
            used. Cannot be combined with `model_route_id`.
          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:
          description: Maximum agent loop steps per turn (default `20`).
          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:
            $ref: "#/components/schemas/AgentStopCondition"
          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:
          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).
          x-naturali-ref: tools
          type: array
          items:
            type: string
        guardrail_ids:
          x-naturali-ref: guardrails
          type: array
          nullable: true
          items:
            type: string
          description: Guardrails attached at the agent scope.
        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).
          type: array
          items:
            $ref: "#/components/schemas/AgentStepRule"
        boundary_policy:
          $ref: "#/components/schemas/AgentBoundaryPolicy"
        temperature:
          description: Sampling temperature passed to the model. Omitted, the provider's default applies.
          type: number
        knowledge_config:
          description: Knowledge search run before every generation; its matches are prepended as reference
            context. See [Knowledge Config](/docs/modules/agents#knowledge-config).
          type: object
          additionalProperties: false
          properties:
            memory_store_ids:
              x-naturali-ref: memory-stores
              type: array
              items:
                type: string
            document_ids:
              x-naturali-ref: documents
              type: array
              items:
                type: string
            document_paths:
              type: array
              items:
                type: string
            tags:
              description: Key-value pairs a result's own `tags` must all contain (exact match). Scopes documents
                and memories alike.
              allOf:
                - $ref: "#/components/schemas/TagBag"
            min_score:
              type: number
              description: >
                Minimum raw cosine similarity (0–1) a vector candidate must reach to be ranked, the
                floor the search endpoint spells `min_similarity`. No default: omitted means no
                floor at all, and every one of the `limit` nearest chunks is injected however weak
                it is.
            rrf_k:
              type: integer
              description: >
                The `k` in the fusion term `1 / (k + rank)`, the same knob knowledge search takes.
                Smaller weights the top of each ranking more heavily. Omitted, the deployment's
                `KNOWLEDGE_RRF_K` applies.
            recency_half_life_days:
              type: number
              description: >
                Half-life in days of the decay applied to **memory** results after fusion, the same
                knob knowledge search takes. `0` disables it. Omitted, the deployment's
                `KNOWLEDGE_RECENCY_HALF_LIFE_DAYS` applies.
            limit:
              type: integer
              description: |
                Maximum number of results to inject. Omitted, 10 are injected.
            write_memory_store_id:
              x-naturali-ref: memory-stores
              type: string
              nullable: true
              description: Public ID of the memory store the agent can write to during generation. When set, a
                write_memory tool is automatically available to the agent.
        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."
        prompt_caching:
          type: object
          nullable: true
          description: "Prompt caching for this agent's turns. `{\"enabled\": true}` marks a cache breakpoint
            at the end of the turn's static prefix — the tool definitions and the instructions
            together — so a provider that caches by explicit breakpoint reads that prefix back on
            every later step of the turn and every later turn of the session instead of being
            charged for it again. 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. An agent
            with no `instructions` has no block to mark and caches nothing. Cache reads are reported
            as `cached_tokens` and cache writes as `cache_write_tokens` on usage."
          additionalProperties: false
          properties:
            enabled:
              type: boolean
              description: Whether the breakpoint is marked. Defaults to false.
        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
          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
          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)."
      additionalProperties: false
      properties:
        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.
          x-naturali-ref: ai-providers
          type: string
          nullable: true
        model_route_id:
          x-naturali-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:
          description: Display name; `null` clears it.
          type: string
          nullable: true
        instructions:
          description: System instructions, sent as the system message of every generation; `null` clears them.
          type: string
          nullable: true
        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`.
          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:
          description: Maximum agent loop steps per turn; `null` restores the default of `20`.
          type: integer
          nullable: true
        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
          nullable: true
          items:
            $ref: "#/components/schemas/AgentStopCondition"
          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:
          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).
          x-naturali-ref: tools
          type: array
          nullable: true
          items:
            type: string
        guardrail_ids:
          x-naturali-ref: guardrails
          type: array
          nullable: true
          items:
            type: string
          description: Guardrails attached at the agent scope.
        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).
          type: array
          nullable: true
          items:
            $ref: "#/components/schemas/AgentStepRule"
        boundary_policy:
          $ref: "#/components/schemas/AgentBoundaryPolicy"
        temperature:
          description: Sampling temperature passed to the model; `null` restores the provider's default.
          type: number
          nullable: true
        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).
          type: object
          nullable: true
          additionalProperties: false
          properties:
            memory_store_ids:
              x-naturali-ref: memory-stores
              type: array
              items:
                type: string
            document_ids:
              x-naturali-ref: documents
              type: array
              items:
                type: string
            document_paths:
              type: array
              items:
                type: string
            tags:
              description: Key-value pairs a result's own `tags` must all contain (exact match). Scopes documents
                and memories alike.
              allOf:
                - $ref: "#/components/schemas/TagBag"
            min_score:
              type: number
              description: >
                Minimum raw cosine similarity (0–1) a vector candidate must reach to be ranked, the
                floor the search endpoint spells `min_similarity`. No default: omitted means no
                floor at all, and every one of the `limit` nearest chunks is injected however weak
                it is.
            rrf_k:
              type: integer
              description: >
                The `k` in the fusion term `1 / (k + rank)`, the same knob knowledge search takes.
                Smaller weights the top of each ranking more heavily. Omitted, the deployment's
                `KNOWLEDGE_RRF_K` applies.
            recency_half_life_days:
              type: number
              description: >
                Half-life in days of the decay applied to **memory** results after fusion, the same
                knob knowledge search takes. `0` disables it. Omitted, the deployment's
                `KNOWLEDGE_RECENCY_HALF_LIFE_DAYS` applies.
            limit:
              type: integer
              description: |
                Maximum number of results to inject. Omitted, 10 are injected.
            write_memory_store_id:
              x-naturali-ref: memory-stores
              type: string
              nullable: true
              description: Public ID of the memory store the agent can write to during generation. When set, a
                write_memory tool is automatically available to the agent.
        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."
        prompt_caching:
          type: object
          nullable: true
          description: "Prompt caching for this agent's turns. `{\"enabled\": true}` marks a cache breakpoint
            at the end of the turn's static prefix — the tool definitions and the instructions
            together — so a provider that caches by explicit breakpoint reads that prefix back on
            every later step of the turn and every later turn of the session instead of being
            charged for it again. 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. An agent
            with no `instructions` has no block to mark and caches nothing. Cache reads are reported
            as `cached_tokens` and cache writes as `cache_write_tokens` on usage."
          additionalProperties: false
          properties:
            enabled:
              type: boolean
              description: Whether the breakpoint is marked. Defaults to false.
        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
          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
          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
        expected_version:
          description: Refuses the write unless the resource is at this version.
          allOf:
            - $ref: "#/components/schemas/ExpectedVersion"
    CreateAgentGenerationRequest:
      type: object
      required:
        - messages
      additionalProperties: false
      properties:
        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`."
          type: array
          minItems: 1
          items:
            type: object
            additionalProperties: false
            required:
              - role
              - content
            properties:
              role:
                type: string
                enum:
                  - user
                  - assistant
              content:
                oneOf:
                  - type: string
                  - $ref: "#/components/schemas/ToolOutputMessageContent"
                  - $ref: "#/components/schemas/DocumentMessageContent"
        stream:
          type: boolean
          default: false
          x-naturali-tool-unsupported: true
          description: When true the response is an SSE stream
        trace_id:
          x-naturali-ref: traces
          type: string
          x-naturali-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-naturali-ref: traces
          type: string
          nullable: true
          x-naturali-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-naturali-ref: traces
          type: string
          nullable: true
          x-naturali-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-naturali-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-Naturali-Context-<key>` headers on every `http` and
            `mcp` tool call in this generation. The header name is the deployment's configured
            context prefix (`X-Naturali-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:
          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."
          allOf:
            - $ref: "#/components/schemas/NullableMetadataBag"
        idempotency_key:
          type: string
          maxLength: 255
          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.


            Every 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.
          example: discord-1287654321098765432
        knowledge_config:
          type: object
          nullable: true
          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.
          additionalProperties: false
          properties:
            memory_store_ids:
              x-naturali-ref: memory-stores
              type: array
              items:
                type: string
            document_ids:
              x-naturali-ref: documents
              type: array
              items:
                type: string
            document_paths:
              type: array
              items:
                type: string
            tags:
              description: Key-value pairs a result's own `tags` must all contain (exact match). Scopes documents
                and memories alike.
              allOf:
                - $ref: "#/components/schemas/TagBag"
            min_score:
              type: number
              description: >
                Minimum raw cosine similarity (0–1) a vector candidate must reach to be ranked, the
                floor the search endpoint spells `min_similarity`. No default: omitted means no
                floor at all, and every one of the `limit` nearest chunks is injected however weak
                it is.
            rrf_k:
              type: integer
              description: >
                The `k` in the fusion term `1 / (k + rank)`, the same knob knowledge search takes.
                Smaller weights the top of each ranking more heavily. Omitted, the deployment's
                `KNOWLEDGE_RRF_K` applies.
            recency_half_life_days:
              type: number
              description: >
                Half-life in days of the decay applied to **memory** results after fusion, the same
                knob knowledge search takes. `0` disables it. Omitted, the deployment's
                `KNOWLEDGE_RECENCY_HALF_LIFE_DAYS` applies.
            limit:
              type: integer
              description: |
                Maximum number of results to inject. Omitted, 10 are injected.
    ToolOutputMessageContent:
      type: object
      required:
        - type
        - tool_id
      additionalProperties: false
      properties:
        type:
          type: string
          enum:
            - tool_output
        tool_id:
          x-naturali-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 `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
      additionalProperties: false
      properties:
        type:
          type: string
          enum:
            - document
        document_id:
          x-naturali-ref: documents
          type: string
          description: Public ID of a document to use as the message content.
    SubmitToolOutputsRequest:
      type: object
      required:
        - tool_outputs
      additionalProperties: false
      properties:
        tool_outputs:
          description: Results for the tool calls the paused generation is waiting on, one entry per
            `tool_call_id`.
          type: array
          minItems: 1
          items:
            type: object
            required:
              - tool_call_id
              - output
            additionalProperties: false
            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
        /v1/projects/{project_id}/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-naturali-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
    AgentStopCondition:
      type: object
      additionalProperties: false
      required:
        - type
      description: One stop condition. `has_tool_call` ends the turn after the step that calls
        `tool_name`; `max_chain_generations` bounds the continuation chain at `max_generations`.
      properties:
        type:
          type: string
          enum:
            - has_tool_call
            - max_chain_generations
          description: Which scope the condition ends.
        tool_name:
          type: string
          nullable: true
          description: The resolved tool name a `has_tool_call` condition matches. Required for that type.
        max_generations:
          type: integer
          nullable: true
          description: Generations the continuation chain may reach before further resumptions stop with
            `chain_limit`. Required for `max_chain_generations`, and must be a positive integer.
    AgentStepRule:
      type: object
      additionalProperties: false
      required:
        - step
      description: One per-step override. Steps not named by a rule use the agent's own `tool_choice` and
        `active_tool_ids`.
      properties:
        step:
          type: integer
          description: The 1-indexed step of the turn this rule applies to. The numbering spans a
            `requires_action` pause.
        tool_choice:
          description: 'Tool choice for this step — `auto`, `required`, `null`, or `{ "type": "tool",
            "tool_name": "search" }`. Stored verbatim.'
        active_tool_ids:
          x-naturali-ref: tools
          type: array
          nullable: true
          items:
            type: string
          description: Tool IDs active on this step.
    AgentBoundaryPolicy:
      type: object
      nullable: true
      additionalProperties: false
      required:
        - statement
      description: Restricts which runtime actions the agent may invoke. Evaluated as the intersection
        with the caller's own policy, so it can only narrow.
      properties:
        statement:
          type: array
          items:
            $ref: "#/components/schemas/AgentBoundaryPolicyStatement"
          description: IAM policy statements, in the policy document grammar.
    AgentBoundaryPolicyStatement:
      type: object
      additionalProperties: false
      required:
        - effect
        - action
      properties:
        effect:
          type: string
          enum:
            - Allow
            - Deny
          example: Allow
        action:
          type: array
          items:
            type: string
          example:
            - memories:*
            - agents:DeleteAgent
        resource:
          type: array
          nullable: true
          items:
            type: string
          description: Resource SRN patterns. Omit to match every resource.
          example:
            - srn:proj_abc:memories:*
        condition:
          type: object
          nullable: true
          additionalProperties: true
          description: "Condition block, in the same grammar a policy document uses: keys are condition
            operators mapping to context-key/value maps."
          example:
            StringEquals: {}
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          description: Structured error. Every error response uses this shape, so `code` can be read without
            first checking the type of `error`.
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: "Machine-readable error code: lower_snake for an error naturali raises
                (`access_denied`), UPPER_SNAKE for one the runtime reports (`RESOURCE_NOT_FOUND`)."
              example: access_denied
            message:
              type: string
              description: Human-readable explanation.
              example: Your role in this project does not carry this action.
            details:
              type: object
              additionalProperties: true
              description: Structured context for an error naturali raises, such as the `resource` and `limit` of
                a `plan_limit_reached`.
            meta:
              type: object
              description: Structured context for an error the runtime reports.
    TagBag:
      type: object
      additionalProperties:
        type: string
      x-cli-flag-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 and knowledge search.


        Keys 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.


        The 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.
      example:
        team: finance
        env: prod
    CreateToolRequest:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          description: Tool name
        type:
          type: string
          enum:
            - http
            - client
            - mcp
            - pipeline
          description: Tool type (default http)
        description:
          type: string
          description: What the tool does
        parameters:
          type: object
          description: JSON Schema for tool input
        execute:
          $ref: "#/components/schemas/ToolExecuteConfig"
        mcp:
          $ref: "#/components/schemas/ToolMcpConfig"
        actions:
          type: array
          items:
            type: string
          description: "Allowlist of actions. 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."
        denied_actions:
          type: array
          items:
            type: string
          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."
        context_keys:
          type: array
          nullable: true
          items:
            type: string
          description: Optional allowlist of `tool_context` keys that may be forwarded to this tool as
            prefixed context headers (`X-Naturali-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.
        preset_parameters:
          type: object
          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.


            Values 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.
        pipeline:
          type: object
          description: Pipeline definition for `pipeline` tools. See the `pipeline` field on the Tool schema
            for the full structure.
        output_mapping:
          type: object
          description: Universal JSON Logic mapping applied to the tool's raw result. See the `output_mapping`
            field on the Tool schema for details.
        guardrail_ids:
          x-naturali-ref: guardrails
          type: array
          nullable: true
          items:
            type: string
          description: Guardrails attached at the tool scope.
    ToolExecuteConfig:
      type: object
      nullable: true
      additionalProperties: false
      description: >
        Execution config for http tools. Supported fields: `url` (required), `method` (default
        `POST`), `headers`, and `body_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).


        `auth` adds a computed request credential, for targets whose `Authorization` value cannot be
        expressed as a static header. Supported `auth.type` values:


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

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


        Credential 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.


        A 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.


        `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-Naturali-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.
      properties:
        url:
          type: string
          description: Endpoint URL. May carry `{paramName}` placeholders resolved from the tool arguments at
            call time.
        method:
          type: string
          nullable: true
          description: "HTTP method (default: `POST`)"
        headers:
          type: object
          nullable: true
          additionalProperties:
            type: string
          description: Static headers sent on every request. Values accept `{{secret:...}}` and
            `{{context:<key>}}` references.
        body_mode:
          type: string
          nullable: true
          enum:
            - json
            - multipart
            - null
          description: "Request body encoding. Incompatible with `auth.type: aws_sigv4`."
        auth:
          $ref: "#/components/schemas/ToolExecuteAuthConfig"
    ToolExecuteAuthConfig:
      type: object
      nullable: true
      additionalProperties: false
      description: Computed request credential, for a target whose `Authorization` value cannot be
        expressed as a static header. `aws_sigv4` requires `region`, `service`, `access_key_id` and
        `secret_access_key`; `gcp_service_account` requires `credentials` and `scopes`. Every
        credential field accepts a `{{secret:...}}` reference and should carry one, since a literal
        is stored as written and masked on read.
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - aws_sigv4
            - gcp_service_account
          description: Which credential is computed.
        region:
          type: string
          nullable: true
          description: "`aws_sigv4`: the signing region, e.g. `us-east-1`."
        service:
          type: string
          nullable: true
          description: "`aws_sigv4`: the signing service, e.g. `execute-api`."
        access_key_id:
          type: string
          nullable: true
          description: "`aws_sigv4`: the access key id."
        secret_access_key:
          type: string
          nullable: true
          description: '`aws_sigv4`: the secret access key. Masked on read as `{"no_echo": true}` when written
            as a literal.'
        session_token:
          type: string
          nullable: true
          description: "`aws_sigv4`: the session token of a temporary credential. Masked on read, like
            `secret_access_key`."
        credentials:
          type: string
          nullable: true
          description: "`gcp_service_account`: the service account key file JSON, as a string. Masked on read,
            like `secret_access_key`."
        scopes:
          type: array
          nullable: true
          items:
            type: string
          description: "`gcp_service_account`: the OAuth scopes to mint the token for."
    ToolMcpConfig:
      type: object
      nullable: true
      additionalProperties: false
      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`.
      properties:
        url:
          type: string
          description: MCP server URL. Accepts a `{{secret:...}}` reference.
        headers:
          type: object
          nullable: true
          additionalProperties:
            type: string
          description: Headers sent on every MCP request. Values accept `{{secret:...}}` and
            `{{context:<key>}}` references.
    ExpectedVersion:
      type: integer
      minimum: 1
      nullable: true
      description: >-
        The version the caller believes the resource holds. When the resource is at any other
        version the write is refused with `409 VERSION_CONFLICT` and nothing is written;
        `meta.current_version` on that response names the version in force.


        Omit it to write unconditionally. Omitting it does not make the write unordered: two writes
        that reach the server together are still serialized, and the one whose version was taken
        first is refused the same way. What the field adds is refusing a write whose author read the
        resource some time ago and has not seen what happened since.


        The `If-Match` header carries the same precondition for a client that prefers the HTTP
        spelling. Sending both with different versions is `400 VALIDATION_FAILED`.
      example: 3
    NullableMetadataBag:
      type: object
      nullable: true
      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.
      example:
        author: John
        revision: 2
  parameters:
    ProjectId:
      name: project_id
      in: path
      required: true
      description: Project public ID (proj_ prefix).
      schema:
        type: string
        example: proj_V1StGXR8Z5jdHi6B
    AgentId:
      name: agent_id
      in: path
      required: true
      description: Agent public ID
      schema:
        type: string
        example: agent_V1StGXR8Z5jdHi6B
    GenerationId:
      name: generation_id
      in: path
      required: true
      description: Public ID of the generation paused at `requires_action`
      schema:
        type: string
        example: gen_V1StGXR8Z5jdHi6B
    AgentVersionNumber:
      name: version
      in: path
      required: true
      description: Archived config version number
      schema:
        type: integer
        minimum: 1
    IfMatchVersion:
      name: If-Match
      in: header
      required: false
      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`.
      schema:
        type: string
      example: "3"
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: A naturali API key (nat_sk_…) or a session JWT.
    oauth2:
      type: oauth2
      description: "A connected app's OAuth access token, issued by this API's authorization server
        (discovery: /.well-known/oauth-authorization-server). Its one scope carries every operation,
        confined to the projects the user chose when approving the app."
      flows:
        authorizationCode:
          authorizationUrl: https://api.naturali.ai/authorize
          tokenUrl: https://api.naturali.ai/token
          refreshUrl: https://api.naturali.ai/token
          scopes:
            mcp:access: Every operation this API serves, on the projects the grant covers.
  responses:
    VersionConflict:
      description: "The resource has moved past the version this write read (`VERSION_CONFLICT`): a stated
        `expected_version` or `If-Match` no longer matches, or a concurrent write took the version
        first. `meta.current_version` names the version in force. Nothing is written."
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
