# AUTO-GENERATED tier-A mirror spec — do not edit.
# Source: openapi/runtime/generations.yaml (runtime 0.71.2)
#  + policy: openapi/mirror-policies/generations.json
# Regenerate: node scripts/generate-mirror-specs.mjs
openapi: 3.0.3
info:
  title: naturali.ai — Generations API
  version: 1.0.0
  description: >-
    Generations: one model loop each — the record an agent, session or conversation generation is
    polled and audited through, with its transcript and its content-purge control. 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: Generations
    description: Inspect generation records
security:
  - bearerAuth: []
  - oauth2:
      - mcp:access
paths:
  /v1/projects/{project_id}/generations:
    get:
      tags:
        - Generations
      summary: List generations
      description: >
        Returns generations the caller can access, optionally filtered by agent, trace,
        orchestration run, node, and status. The generations of one trace are the `trace_id` filter.


        Filtering by `orchestration_run_id` is the supported way to get from an orchestration run to
        the generations its agent nodes produced: a node execution record carries no generation id,
        so the pointer lives here, alongside the run's other attribution columns.
      operationId: listGenerations
      parameters:
        - name: agent_id
          in: query
          required: false
          description: Filter by agent public ID
          schema:
            type: string
        - name: trace_id
          in: query
          required: false
          description: Filter by trace public ID
          schema:
            type: string
        - name: session_id
          in: query
          required: false
          description: >
            Return only the generations dispatched through one session — the turns behind that
            conversation's spend. A session that does not exist in scope yields an empty page.
          schema:
            type: string
        - name: actor_id
          in: query
          required: false
          description: >
            Return only the generations one end user started, across every session they appear in.
            An actor that does not exist in scope yields an empty page.
          schema:
            type: string
        - name: initiator_generation_id
          in: query
          required: false
          description: >
            Filter by the public ID of the parent generation. Returns every generation started by
            that generation: sub-agent invocations, approval continuations, client-tool re-handoffs
            and memory-rule handler turns. Top-level generations are not returned.
          schema:
            type: string
        - name: chain_id
          in: query
          required: false
          description: >
            Filter by the continuation chain the generation belongs to. This is how a chain is
            expanded into its members — the chain record carries only their count.
          schema:
            type: string
        - name: orchestration_run_id
          in: query
          required: false
          description: >
            Filter by the orchestration run that dispatched the generation. This is how a run is
            traced back to what its agent nodes did — a node execution record stores no generation
            id.
          schema:
            type: string
        - name: node_id
          in: query
          required: false
          description: >
            Filter by the orchestration node that dispatched the generation. Combine with
            `orchestration_run_id` to narrow to one node of one run; a retried node returns one
            generation per `node_attempt`.
          schema:
            type: string
        - name: status
          in: query
          required: false
          description: Filter by lifecycle status
          schema:
            type: string
            enum:
              - in_progress
              - requires_action
              - completed
              - failed
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
        - name: offset
          in: query
          required: false
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: Paginated list of generations
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Generation"
                  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}/generations/{generation_id}:
    get:
      tags:
        - Generations
      summary: Get a generation
      description: >
        Returns a single generation record by ID, including its status and the structured `error`
        payload when the generation failed (e.g. because the upstream AI provider returned an
        error).
      operationId: getGeneration
      x-naturali-resource:
        kind: generation
        from: generation_id
      parameters:
        - name: generation_id
          in: path
          required: true
          schema:
            type: string
          description: Public ID of the generation
      responses:
        "200":
          description: Generation details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Generation"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Generation not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    patch:
      tags:
        - Generations
      summary: Update generation metadata
      description: >
        Attaches caller-supplied key/value metadata to a generation record for per-run audit
        attribution (e.g. the ticket or case an AI action belongs to). The provided keys are
        shallow-merged over the existing `metadata`, so repeated patches accumulate. The bag is
        caller-owned and no key is reserved: server-owned state (usage attribution, the served agent
        version, the route's record, the extraction summary, what knowledge retrieval served) lives
        in its own top-level fields and cannot be written from here.
      operationId: updateGeneration
      x-naturali-resource:
        kind: generation
        from: generation_id
      parameters:
        - name: generation_id
          in: path
          required: true
          schema:
            type: string
          description: Public ID of the generation
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateGenerationRequest"
            examples:
              audit:
                summary: Attach caller audit metadata
                value:
                  metadata:
                    team: payments
                    ticket_id: OPS-4821
      responses:
        "200":
          description: Updated generation
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Generation"
        "400":
          description: Bad Request (e.g. metadata is not a JSON object)
          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: Generation not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/generations/{generation_id}/content:
    delete:
      tags:
        - Generations
      summary: Purge generation content
      description: >
        Clears the generation's content — `metadata`, `error`, `extraction`, and the internal
        recovery state of a paused run — and stamps `content_redacted_at`.


        The usage and audit skeleton is preserved: ids, timestamps, status, stop reason, and the
        attribution fields (`action_id`, `trigger_id`, `orchestration_run_id`, `node_id`,
        `node_attempt`, `agent_version`, `routing`) the billing ledger reads. A purged generation
        reads back as that skeleton, not a 404.


        This does **not** delete the parent trace's steps object, which holds this generation's
        content alongside its siblings'. To erase the run's content completely, purge the trace
        (`DELETE /v1/projects/{project_id}/traces/{trace_id}/content`), which cascades here.


        Idempotent — purging an already-purged generation succeeds and leaves the original
        `content_redacted_at` in place.
      operationId: purgeGenerationContent
      x-naturali-resource:
        kind: generation
        from: generation_id
      parameters:
        - name: generation_id
          in: path
          required: true
          schema:
            type: string
          description: Public ID of the generation
      responses:
        "200":
          description: The purged generation skeleton
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Generation"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Generation not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/generations/{generation_id}/transcript:
    get:
      tags:
        - Generations
      summary: Get a generation's transcript
      description: >
        Returns one generation's turn read back as an ordered sequence of steps: what it was asked,
        each model step with its tool calls and results, and how it ended.


        The transcript is assembled at read time from the generation record and the trace's steps
        object; nothing is stored, so it cannot outlive the content it projects. Requires
        `traces:GetTrace` in addition to `generations:GetGeneration`, because the response merges
        content from both resources.


        A generation whose content is unavailable — never written under zero-retention, or cleared
        by a purge — returns `200` with the skeleton rather than an error: `input` and `output` are
        null, `steps` is empty, and the `content_redacted_*` fields say which happened.
        `content_redacted_by_principal_id` is `zero_retention` when the content was never stored,
        and the purging principal's ID when it was erased later. A generation that is still running
        returns the same shape with an empty `steps`; `status` disambiguates the two.
      operationId: getGenerationTranscript
      x-naturali-resource:
        kind: generation
        from: generation_id
      parameters:
        - name: generation_id
          in: path
          required: true
          schema:
            type: string
          description: Public ID of the generation
      responses:
        "200":
          description: The generation's transcript
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GenerationTranscript"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Generation not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
components:
  schemas:
    Generation:
      type: object
      properties:
        id:
          type: string
          description: Public ID of the generation
          example: gen_V1StGXR8Z5jdHi6B
        project_id:
          x-naturali-ref: projects
          type: string
          description: Public ID of the project
        agent_id:
          x-naturali-ref: agents
          type: string
          description: Public ID of the agent that ran this generation
        trace_id:
          x-naturali-ref: traces
          type: string
          description: Public ID of the trace this generation belongs to
        initiator_generation_id:
          x-naturali-ref: generations
          type: string
          nullable: true
          description: >
            Public ID of the generation that started this one: a sub-agent invocation, an approval
            continuation, a client-tool re-handoff or a memory-rule handler turn. Null for top-level
            generations.
        chain_id:
          x-naturali-ref: chains
          type: string
          nullable: true
          description: >
            Public ID of the continuation chain this generation belongs to. Set on every member of a
            chain — the continuations and the root they descend from — and null on a generation that
            is not part of one.
        conversation_id:
          x-naturali-ref: conversations
          type: string
          nullable: true
          description: >
            Public ID of the conversation this turn served, or null for a generation started outside
            one — a direct call, a trigger, an orchestration node. This is the edge a memory
            assertion walks up to reach the conversation a fact was learned in.
        session_id:
          x-naturali-ref: sessions
          type: string
          nullable: true
          description: >
            Public ID of the session this generation was dispatched through, or null for a
            generation started outside one. This is the link a per-session cost reading follows; the
            session's own `usage` field reports the roll-up directly.
        actor_id:
          x-naturali-ref: actors
          type: string
          nullable: true
          description: >
            Public ID of the end-user actor the generation was attributed to, or null when none was.
            Copied onto the generation's usage event.
        started_by_principal_type:
          type: string
          nullable: true
          description: Type of the principal that started the generation
        started_by_principal_id:
          type: string
          nullable: true
          description: ID of the principal that started the generation
        status:
          type: string
          description: Lifecycle status of the generation
          enum:
            - in_progress
            - requires_action
            - completed
            - failed
          example: failed
        started_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
          nullable: true
          description: When the generation reached a terminal state
        last_activity_at:
          type: string
          format: date-time
          nullable: true
        stop_reason:
          type: string
          nullable: true
          description: >
            Why the generation stopped. Either the model provider's own finish reason relayed
            unchanged ('stop', 'tool-calls', 'length', …) or one the platform names itself:
            'max_steps' when the turn spent its whole step budget on tool calls, 'depth_guard' when
            a nested call exceeded the call depth, 'chain_limit' when a continuation chain reached
            its generation budget, or 'error' when the turn failed.
          example: error
        error:
          type: object
          nullable: true
          description: >
            Structured error payload recorded when the generation failed. Contains at least
            `message`; `code` is set for mapped errors (e.g. AI_PROVIDER_ERROR for upstream provider
            failures).
          properties:
            code:
              type: string
              example: AI_PROVIDER_ERROR
            message:
              type: string
              example: "Provider returned 402: insufficient credits"
            meta:
              type: object
        action_id:
          type: string
          nullable: true
          description: >
            Logical action label supplied on the generate request. Recorded on the generation's
            usage event for per-action spend rollups.
        trigger_id:
          x-naturali-ref: triggers
          type: string
          nullable: true
          description: Trigger that initiated the generation, when applicable
        orchestration_run_id:
          x-naturali-ref: orchestration-runs
          type: string
          nullable: true
          description: |
            Orchestration run that dispatched the generation. Null for a standalone generation.
        node_id:
          type: string
          nullable: true
          description: >
            Node within `orchestration_run_id` that dispatched the generation. Together with the run
            it forms the usage event's replay identity.
        node_attempt:
          type: integer
          nullable: true
          description: >
            The node's 1-based retry attempt, completing the run + node + attempt replay identity. A
            retried node produces one generation per attempt; this is what tells them apart. Null
            for a generation no orchestration node dispatched.
        agent_version:
          type: integer
          nullable: true
          description: >
            Agent config version that served this generation, resolved by the served-version
            resolver (see [agent versions](/docs/modules/agents#versions-and-releases)).
        extraction:
          type: object
          nullable: true
          description: >
            What each [memory rule](/docs/modules/memories#memory-rules) bound to
            `agents.generation.completed` wrote for this turn, keyed by the rule's id — a store may
            have several rules, and one flat pair of counts could not say which produced them.
            Absent when no rule fired. Rules bound to `conversations.message.generated` are recorded
            in `memory_assertions` only. The rows behind every count are in `memory_assertions`, so
            the summary and what it summarizes can be reconciled.
          additionalProperties:
            type: object
            properties:
              candidates:
                type: integer
                description: Number of candidates the rule's handler proposed
              created:
                type: integer
                description: Number of new memories created
              superseded:
                type: integer
                description: >
                  Number of candidates that restated a known fact that had changed, retiring the
                  memory holding it
              skipped:
                type: integer
                description: Number of candidates skipped (e.g. duplicates)
        memory_assertions:
          type: array
          description: >
            Every memory write this turn made, oldest first — each memory rule's firings and the
            agent's own `write_memory` calls alike, which the `extraction` counts never covered.
            Present on the single read only; a listing would make it one extra query per generation.
          items:
            $ref: "#/components/schemas/MemoryAssertion"
        tool_surface:
          type: object
          nullable: true
          description: >-
            What this turn's tool definitions cost to send — the one part of a prompt no provider
            reports and no caller can derive, since the tool block is a constant inside the reported
            totals and identical on every step.


            Null when the surface was never measured: a generation from before the field, or a path
            that resolves none. That is **not** what `tools: 0` means, which is a measured agent
            with nothing bound.


            Not a meter. `estimated_tokens` is an estimate and is never priced — `usage` and the
            usage events are the billing record.
          properties:
            tools:
              type: integer
              description: Tools resolved for the turn, before any per-step narrowing by `step_rules`.
              example: 61
            bytes:
              type: integer
              description: Serialized length of those definitions — name, description and input schema — in
                canonical JSON, not the provider's wire format. Exact, and comparable across
                providers, which is what makes two agents' surfaces worth comparing.
              example: 152161
            estimated_tokens:
              type: integer
              description: "`bytes` over a measured bytes-per-token ratio. An estimate, and named one."
              example: 45148
        retrieval:
          type: array
          nullable: true
          description: >-
            What `knowledge_config` retrieval injected into this turn, in the order it was injected:
            which document, at which version, and which chunk, or which memory. Written by the
            server at turn start and never writable; pointers only, never the text.


            Null when no retrieval ran — the agent has no `knowledge_config`, or the turn had
            neither a query nor a filter to search with. An empty array is a retrieval that ran and
            matched nothing.


            Not content: a content purge leaves it standing, and zero retention still writes it.
            `document_version` names an archived version, so the text the turn read stays readable
            after the document changes.
          items:
            oneOf:
              - $ref: "#/components/schemas/GenerationRetrievedDocument"
              - $ref: "#/components/schemas/GenerationRetrievedMemory"
            discriminator:
              propertyName: source_type
              mapping:
                document: "#/components/schemas/GenerationRetrievedDocument"
                memory: "#/components/schemas/GenerationRetrievedMemory"
        idempotency_key:
          type: string
          nullable: true
          description: The deduplication key the generation was started under, unique within the project and
            claimed for as long as the generation record exists. Null for a generation started
            without one.
          example: discord-1287654321098765432
        usage:
          allOf:
            - $ref: "#/components/schemas/UsageTotals"
          nullable: true
          description: >-
            What the turn cost: token counts and `cost_usd` for every event metered against this
            generation. Present on the single-generation read; omitted from the listing, where it
            would be a query per row.


            Null when nothing has been metered yet — a turn still in flight, or one whose metering
            failed. A turn is metered once at the end, so this is one event's figures in the
            ordinary case.
        routing:
          type: object
          nullable: true
          description: >
            What the model route did for this generation. Present only when the agent resolves its
            model through a `model_route_id`.
          properties:
            route_id:
              x-naturali-ref: model-routes
              type: string
              description: The route that resolved the model
            target_index:
              type: integer
              nullable: true
              description: >
                Position in the route's `targets` of the target that served the last LLM call; null
                when every attempt failed.
            fallbacks:
              type: integer
              description: >
                How many times the route moved past a target during this generation (cumulative
                across a multi-step run).
            attempts:
              type: array
              description: >
                Every attempt, in order, across every LLM call of the run. An attempt with no
                `error_class` succeeded.
              items:
                type: object
                properties:
                  target_index:
                    type: integer
                  ai_provider_id:
                    x-naturali-ref: ai-providers
                    type: string
                  model:
                    type: string
                  error_class:
                    type: string
                    enum:
                      - provider_error
                      - timeout
                      - rate_limited
                    description: >
                      Why the attempt failed. Absent on the serving attempt, and absent on a
                      deterministic failure (which fails the generation instead of failing over).
        metadata:
          description: >
            Caller-owned key/value annotations, attached at create time (via the `metadata` field on
            the create-agent-generation request) or afterwards (via the update-generation request),
            and returned verbatim. The server writes nothing here: every piece of state it owns —
            usage attribution, the served agent version, the route's record, the extraction summary,
            internal recovery state — is a field of its own, so no key written here can reach
            platform state. Keys are never transformed, and no key is reserved.
          example:
            team: payments
            ticket_id: OPS-4821
          allOf:
            - $ref: "#/components/schemas/NullableMetadataBag"
        content_redacted_at:
          type: string
          format: date-time
          nullable: true
          description: >
            When the generation's content was purged. Non-null means `metadata`, `error`,
            `extraction` and the internal recovery state have been cleared, while the usage/audit
            skeleton — ids, timestamps, status, stop reason and the attribution fields — is
            preserved.
        content_redacted_by_principal_type:
          type: string
          nullable: true
          description: Principal kind that purged the content ('user' or 'api_key')
          example: user
        content_redacted_by_principal_id:
          type: string
          nullable: true
          description: >
            Public ID of the principal that purged the content — the API key's own id for key auth,
            so the record names which key acted.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    GenerationRetrievedDocument:
      type: object
      required:
        - source_type
        - document_id
        - document_version
        - chunk_id
        - page
        - similarity_score
      properties:
        source_type:
          type: string
          enum:
            - document
          example: document
        document_id:
          x-naturali-ref: documents
          type: string
          description: Public ID of the document the chunk belongs to
          example: doc_V1StGXR8Z5jdHi6B
        document_version:
          type: integer
          nullable: true
          description: The document version the chunk was read from. Read it back with the document's versions
            to see the exact text the turn was given.
          example: 3
        chunk_id:
          type: string
          description: Public ID of the injected chunk. Stops resolving once the document is re-chunked;
            `document_version` is the durable pointer.
          example: dchunk_V1StGXR8Z5jdHi6B
        page:
          type: integer
          nullable: true
          description: Page within the source PDF (1-indexed). Null for plain text.
          example: 2
        similarity_score:
          type: number
          nullable: true
          description: Raw cosine similarity (0–1) between the turn's query and the result. Null when the turn
            had no query, or the search answered from the lexical channel alone. The rank is the
            array order.
          example: 0.82
    GenerationRetrievedMemory:
      type: object
      required:
        - source_type
        - memory_store_id
        - memory_id
        - similarity_score
      properties:
        source_type:
          type: string
          enum:
            - memory
          example: memory
        memory_store_id:
          x-naturali-ref: memory-stores
          type: string
          description: Public ID of the memory store the memory belongs to
          example: mstore_V1StGXR8Z5jdHi6B
        memory_id:
          type: string
          description: Public ID of the injected memory
          example: mem_V1StGXR8Z5jdHi6B
        similarity_score:
          type: number
          nullable: true
          description: Raw cosine similarity (0–1) between the turn's query and the result. Null when the turn
            had no query, or the search answered from the lexical channel alone. The rank is the
            array order.
          example: 0.82
    UpdateGenerationRequest:
      type: object
      required:
        - metadata
      additionalProperties: false
      properties:
        metadata:
          description: >
            Caller-supplied key/value metadata to shallow-merge into the generation record's
            caller-owned `metadata` bag. No key is reserved: server-owned state lives in its own
            top-level fields and cannot be written from here.
          example:
            team: payments
            ticket_id: OPS-4821
          allOf:
            - $ref: "#/components/schemas/MetadataBag"
    TranscriptToolCall:
      type: object
      description: One tool call the model made during a step.
      properties:
        id:
          type: string
          nullable: true
          description: >
            The call's ID, as the model provider issued it (e.g. `call_…`), used to correlate it
            with an entry in `tool_results`. Null when the stored step did not record one.
        tool_name:
          type: string
          nullable: true
          example: weather
        args:
          description: >
            The arguments the model supplied, as a value. This payload is tool-owned: its keys are
            passed through exactly as they were recorded and are never inspected or rewritten by the
            runtime.
          example:
            cityName: Paris
    TranscriptToolResult:
      type: object
      description: >
        One tool's answer to a call in the same step. A call that failed is reported here too, with
        `result` null and `error` set — so a reader sees successes and failures in one ordered list
        keyed by the call they answer.
      properties:
        tool_call_id:
          type: string
          nullable: true
          description: The `id` of the `tool_calls` entry this answers.
        tool_name:
          type: string
          nullable: true
          example: weather
        result:
          description: >
            What the tool returned, as a value. Tool-owned: keys are passed through verbatim. Null
            when the call errored.
          example:
            tempC: 18
        error:
          description: The tool's failure, when the step recorded one.
          example: null
    TranscriptStep:
      type: object
      description: >
        One model step. Projected from the stored step at read time — the stored shape is provider-
        and SDK-specific and is never put on the wire.
      properties:
        index:
          type: integer
          description: >
            Zero-based position of this step in the turn. Positional rather than the model's own
            step number, which restarts at zero when a paused turn resumes.
          example: 0
        text:
          type: string
          description: |
            The text this step produced. Empty for a step that only called tools.
          example: ""
        finish_reason:
          type: string
          nullable: true
          description: Why this step stopped.
          example: tool-calls
        tool_calls:
          type: array
          items:
            $ref: "#/components/schemas/TranscriptToolCall"
        tool_results:
          type: array
          items:
            $ref: "#/components/schemas/TranscriptToolResult"
        usage:
          allOf:
            - $ref: "#/components/schemas/UsageTotals"
          nullable: true
          description: What this step reported, in the shape every usage surface uses. Null when the step
            recorded no usage at all; within a step a dimension the provider did not report reads 0.
            `cost_usd` is always null here — the ledger prices one event per generation, so a
            per-step price would disagree with it after any price change.
    GenerationTranscript:
      type: object
      description: >
        One generation's turn, read back step by step. Assembled at read time from the generation
        record and the trace's steps object — never stored, so it dies with the content it projects.
      properties:
        generation_id:
          x-naturali-ref: generations
          type: string
          example: gen_V1StGXR8Z5jdHi6B
        trace_id:
          x-naturali-ref: traces
          type: string
          nullable: true
          example: trace_V1StGXR8Z5jdHi6B
        project_id:
          x-naturali-ref: projects
          type: string
        agent_id:
          x-naturali-ref: agents
          type: string
        agent_version:
          type: integer
          nullable: true
          description: Agent config version that served the turn.
        status:
          type: string
          description: >
            Lifecycle status of the generation. Disambiguates an empty `steps` caused by a run still
            in flight from one caused by erased content.
          enum:
            - in_progress
            - requires_action
            - completed
            - failed
          example: completed
        stop_reason:
          type: string
          nullable: true
          description: >
            Why the generation stopped — the provider's finish reason, or one of the platform's own
            ('max_steps', 'depth_guard', 'chain_limit', 'error').
          example: stop
        started_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
          nullable: true
        step_count:
          type: integer
          description: >
            Number of steps this turn recorded. A counter rather than content, so it survives a
            purge and still reports the size of a turn whose steps are gone. Scoped to the
            generation, not the trace: a trace that groups several generations counts them all in
            its own `step_count`, while each transcript reports only its own.
          example: 2
        input:
          type: array
          nullable: true
          description: >
            The messages the turn was asked, as recorded. Message content is caller-owned and passed
            through verbatim. Null when the content was never stored or has been purged.
          items:
            type: object
        steps:
          type: array
          description: >
            The turn's steps in order. Empty for a run still in progress, and for one whose content
            is unavailable — `status` and `content_redacted_at` say which.
          items:
            $ref: "#/components/schemas/TranscriptStep"
        output:
          type: object
          nullable: true
          description: |
            The turn's final answer. Null when there are no steps to derive it from.
          properties:
            content:
              type: string
              nullable: true
              description: |
                The last step that produced text. Null for a turn that only called tools.
              example: It's 18°C in Paris right now.
            finish_reason:
              type: string
              nullable: true
              description: The finish reason of the actual last step.
              example: stop
        error:
          type: object
          nullable: true
          description: Structured error payload when the generation failed.
        content_redacted_at:
          type: string
          format: date-time
          nullable: true
          description: |
            When the generation's content was erased; null while it is intact.
        content_redacted_by_principal_type:
          type: string
          nullable: true
          description: Principal kind that erased the content.
          example: system
        content_redacted_by_principal_id:
          type: string
          nullable: true
          description: >
            Public ID of that principal. `zero_retention` when the content was never stored,
            distinguishing it from content erased later.
    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.
    MemoryAssertion:
      type: object
      description: "One write attempt against a memory store: some principal, through some mechanism,
        claimed a fact. Append-only, and recorded whatever the outcome — including the skips, which
        left no record anywhere before."
      properties:
        id:
          type: string
          example: massert_V1StGXR8Z5jdHi6B
        memory_store_id:
          x-naturali-ref: memory-stores
          type: string
          example: mstore_V1StGXR8Z5jdHi6B
        memory_id:
          type: string
          nullable: true
          description: "The memory this assertion resolved into: the new memory for `created` and
            `superseded`, the existing memory that matched for `skipped`, the memory withdrawn for
            `retracted`. Null only once that memory has been deleted."
          example: mem_V1StGXR8Z5jdHi6B
        superseded_memory_id:
          type: string
          nullable: true
          description: The memory this assertion retired, on a `superseded` outcome; null otherwise, a
            retraction included — it retires its own memory, which `memory_id` already names. Read
            back from `memories.superseded_by_memory_id` rather than stored here, because validity
            lives on the memory — where every read filters it — and exactly one memory is superseded
            per assertion.
          example: mem_V1StGXR8Z5jdHi6B
        content:
          type: string
          description: "The text as asserted, which is not necessarily the memory's text: a `skipped`
            assertion records what was claimed, while the memory keeps what it already held."
          example: The customer prefers email communication over phone calls
        mechanism:
          type: string
          enum:
            - tool
            - rule
            - api
            - formation
          description: Which door the write came through. `tool` is the agent's `write_memory` call mid-turn;
            `rule` is a post-turn pass over the finished turn; `api` is `POST
            /v1/projects/{project_id}/memories`; `formation` is a `memory` resource in an applied
            template. It answers *how*, never *who* — the principal fields answer that.
          example: tool
        rule_id:
          x-naturali-ref: memory-rules
          type: string
          nullable: true
          description: The memory rule whose firing wrote this, set only when `mechanism` is `rule` — the
            built-in extractor included, since that is a rule with no handler. Null once the rule
            has been deleted, and on assertions written before memory rules shipped.
          example: mrule_V1StGXR8Z5jdHi6B
        generation_id:
          x-naturali-ref: generations
          type: string
          nullable: true
          description: The turn that asserted the fact — the origin of anything an agent wrote, and the edge a
            conversation is reachable from. Null on the `api` and `formation` doors, which have no
            generation behind them.
          example: gen_V1StGXR8Z5jdHi6B
        principal_type:
          type: string
          description: Who claimed the fact, in the vocabulary a generation records its starter with, plus
            `agent`. On both agent doors the principal is the agent itself — the extractor runs
            under its identity.
          example: agent
        principal_id:
          type: string
          example: agent_V1StGXR8Z5jdHi6B
        outcome:
          type: string
          enum:
            - created
            - superseded
            - skipped
            - retracted
          description: What the write resolved to. `created` is a distinct fact, `superseded` is the same fact
            changed (the top match was invalidated and replaced), `skipped` is a fact already known,
            `retracted` is a fact that stopped holding with nothing replacing it.
          example: created
        similarity:
          type: number
          nullable: true
          description: The cosine similarity the outcome was decided against — the top match's, or the
            declared target's when `declared` is true, where it decides nothing and only records how
            far apart the two statements were. Null when there was nothing to compare against, when
            the content could not be embedded, and on a `retracted` outcome, which compares nothing.
          example: 0.97
        declared:
          type: boolean
          description: Whether the write named the memory it replaced (`supersedes` on create) instead of the
            thresholds choosing one. Always false on a `created`, `skipped` or `retracted` outcome.
          example: false
        created_at:
          type: string
          format: date-time
    UsageTotals:
      type: object
      description: >
        Token counts and cost for one slice of the meter. Tokens and cost only: a `compute_second`
        or `gb_day` meter is reported by the aggregate's `components` array.


        The three input dimensions partition the prompt — `input_tokens` = `uncached_input_tokens` +
        `cached_tokens` + `cache_write_tokens` — and are separate because they are separately
        priced. Note that the `input_tokens` **component** on a usage event is the uncached figure
        alone; here `input_tokens` is the whole prompt.
      properties:
        cost_usd:
          type: number
          nullable: true
          description: Sum of priced component costs; null when nothing in the slice was priced. Always null
            at step altitude — the ledger prices one event per generation at write time, and a price
            read back per step would disagree with it after any price change.
        input_tokens:
          type: integer
          description: Full prompt tokens, reconstructed from the components.
        uncached_input_tokens:
          type: integer
          description: Prompt tokens priced at the plain input rate — the prompt minus cache reads and cache
            writes.
        output_tokens:
          type: integer
        cached_tokens:
          type: integer
          description: Prompt tokens served from the provider's prompt cache.
        cache_write_tokens:
          type: integer
          description: Prompt tokens written into the provider's prompt cache.
        reasoning_tokens:
          type: integer
          description: A non-billable subset of `output_tokens`, reported where the provider breaks it out.
    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
    MetadataBag:
      type: object
      description: >-
        Caller-owned annotations on a resource, stored as the object they were written as: the types
        a value was written with are the types a read returns, so a filter can ask an ordering
        question about a number. Unlike other body fields, keys are stored and returned verbatim in
        the casing supplied — they are not converted between snake_case and camelCase.


        No key is reserved, and that is the point: every piece of state the platform owns lives in
        its own typed column, so nothing written here reaches platform state. The platform never
        reads the bag — it is not an IAM context, not a policy input and not part of a prompt —
        which is what separates it from a tag bag.
      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
  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.
