# AUTO-GENERATED tier-A mirror spec — do not edit.
# Source: openapi/runtime/approvals.yaml (runtime 0.71.2)
#  + policy: openapi/mirror-policies/approvals.json
# Regenerate: node scripts/generate-mirror-specs.mjs
openapi: 3.0.3
info:
  title: naturali.ai — Approvals API
  version: 1.0.0
  description: >-
    Approvals: the human decisions a run is waiting on — the queue of pending requests, the
    recurrences that show what keeps asking, and the approve/reject calls that let a paused run
    continue. 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: Approvals
    description: Manage the human-decision approval queue
security:
  - bearerAuth: []
  - oauth2:
      - mcp:access
paths:
  /v1/projects/{project_id}/approvals:
    get:
      tags:
        - Approvals
      summary: List approval items
      description: Returns approval items for a project, filterable by status, origin, and expiry.
      operationId: listApprovals
      parameters:
        - name: status
          in: query
          description: Filter by lifecycle status
          schema:
            type: string
            enum:
              - pending
              - approved
              - rejected
              - expired
        - name: origin
          in: query
          description: Filter by producer origin
          schema:
            type: string
            enum:
              - node
              - tool_call
              - task_transition
        - name: expires_before
          in: query
          description: Return only items expiring at or before this timestamp
          schema:
            type: string
            format: date-time
        - 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 approval items
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - total
                  - limit
                  - offset
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/ApprovalItem"
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        "400":
          description: Invalid `status` or `origin` filter value
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "500":
          description: Internal server error
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/approvals/recurrences:
    get:
      tags:
        - Approvals
      summary: List recurring approval groups
      description: Read-only rollup answering "what keeps coming back?" — groups items by `dedup_key` and
        returns those recurring at least `min_count` times, most-recurrent first. Each group carries
        the ordered item chain (via `previous_item_id`) and the resolution reasons in order, so a
        human can read recurring rejections side by side and graduate the pattern into a guardrail
        `deny`. Exact-key grouping only; no cluster state is stored.
      operationId: listApprovalRecurrences
      parameters:
        - name: status
          in: query
          description: Lifecycle status the groups are built from (default `rejected`)
          schema:
            type: string
            enum:
              - pending
              - approved
              - rejected
              - expired
            default: rejected
        - name: min_count
          in: query
          description: Minimum items in a group for it to be returned
          schema:
            type: integer
            default: 2
            minimum: 1
        - name: limit
          in: query
          required: false
          description: Maximum number of groups to return
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
        - name: offset
          in: query
          required: false
          description: Number of groups to skip
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: List of recurrence groups
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - total
                  - limit
                  - offset
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/ApprovalRecurrenceGroup"
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        "400":
          description: Invalid `status` filter value
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "500":
          description: Internal server error
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/approvals/{approval_id}:
    get:
      tags:
        - Approvals
      summary: Get an approval item
      description: Returns a single approval item with its full evidence.
      operationId: getApproval
      parameters:
        - $ref: "#/components/parameters/approval_id"
      responses:
        "200":
          description: Approval item
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApprovalItem"
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Approval item not found
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/approvals/{approval_id}/approve:
    post:
      tags:
        - Approvals
      summary: Approve an approval item
      description: Approves the item. Optionally supply edited `arguments` to replace the proposed
        arguments (edit-then-approve); the original is preserved on the item. Expiry is re-checked
        at decision time — an expired item can never be approved.
      operationId: approveApproval
      x-naturali-agent-exclude: true
      parameters:
        - $ref: "#/components/parameters/approval_id"
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                arguments:
                  type: object
                  description: "Edited arguments to execute instead of the proposed ones. Editing composes a new call,
                    so it additionally requires permission to make that call: `tools:CallTool` on
                    the tool, and for a `builtin` proposal the action's own IAM action."
                tool_context:
                  type: object
                  additionalProperties:
                    type: string
                  description: "Key-value pairs forwarded as context headers on the approved action and on every tool
                    call of the continuation turn, as on a generation's `tool_context`. Never stored
                    on the item: supply it on the approve call itself, for a tool that authorizes
                    with a credential minted at decision time. The server-pinned identity keys
                    (`session_id`, `actor_id`, `actor_external_id`) are dropped. An invalid or
                    colliding key is rejected with `400 INVALID_TOOL_CONTEXT_KEY` and nothing is
                    resolved."
      responses:
        "200":
          description: Approval item approved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApprovalItem"
        "400":
          description: Edited arguments are not a JSON object, or do not satisfy the tool's parameters schema;
            or a `tool_context` key is not a valid header name
        "401":
          description: Unauthorized
        "403":
          description: Forbidden — including an edit by a caller who may resolve the item but not make the
            call it proposes
        "404":
          description: Approval item not found
        "409":
          description: Item already resolved or expired
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/approvals/{approval_id}/reject:
    post:
      tags:
        - Approvals
      summary: Reject an approval item
      description: Rejects the item. A reason is required and preserved on the item.
      operationId: rejectApproval
      x-naturali-agent-exclude: true
      parameters:
        - $ref: "#/components/parameters/approval_id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - reason
              additionalProperties: false
              properties:
                reason:
                  type: string
                  description: Why the item is being rejected (required)
                  example: Amount exceeds the approved monthly budget.
      responses:
        "200":
          description: Approval item rejected
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApprovalItem"
        "400":
          description: Reason missing
        "401":
          description: Unauthorized
        "403":
          description: Forbidden
        "404":
          description: Approval item not found
        "409":
          description: Item already resolved or expired
    parameters:
      - $ref: "#/components/parameters/ProjectId"
components:
  schemas:
    ApprovalRecurrenceGroup:
      type: object
      description: A set of approval items sharing a `dedup_key` — the same proposed action recurring —
        with the ordered chain and its resolution reasons.
      properties:
        dedup_key:
          type: string
          description: The shared dedup key that defines the group
        agent_id:
          type: string
          nullable: true
          description: Proposing agent (shared across the group)
        tool_id:
          type: string
          nullable: true
          description: Proposed tool (shared across the group)
        count:
          type: integer
          description: Number of items in the group
        chain:
          type: array
          description: The items oldest → newest (the `previous_item_id` chain)
          items:
            type: object
            properties:
              id:
                type: string
                example: apr_V1StGXR8Z5jdHi6B
              status:
                type: string
                enum:
                  - pending
                  - approved
                  - rejected
                  - expired
              resolution_reason:
                type: string
                nullable: true
              created_at:
                type: string
                format: date-time
        reasons:
          type: array
          description: The chain's resolution reasons in order (empty entries omitted)
          items:
            type: string
    ApprovalItem:
      type: object
      properties:
        id:
          type: string
          example: apr_V1StGXR8Z5jdHi6B
        project_id:
          x-naturali-ref: projects
          type: string
        origin:
          type: string
          enum:
            - node
            - tool_call
            - task_transition
          description: How the item was produced (analytics/filtering only)
        status:
          type: string
          enum:
            - pending
            - approved
            - rejected
            - expired
        proposed_action:
          type: object
          nullable: true
          description: The frozen proposed action. Null for producers whose proposal is not a tool call (a
            `task_transition` item gates a workflow transition, named by `task_transition`).
          properties:
            tool_id:
              type: string
            action:
              type: string
              description: Resolved action name (the action name) for `tool_call`-origin items — always present
                there, even for single-action tools. Omitted for `node`-origin items, whose
                downstream execution is wired by a separate `tool` node in the graph.
            arguments:
              type: object
        reasoning:
          type: string
          nullable: true
          description: The proposing agent's rationale
        evidence:
          type: object
          nullable: true
          description: Supporting structured data
        predicted_impact:
          type: string
          nullable: true
          description: Expected execution effect
        expires_at:
          type: string
          format: date-time
          description: Server-enforced hard gate; the item can never execute after this
        dedup_key:
          type: string
          nullable: true
          description: Set on tool-call items to suppress duplicate proposals
        orchestration_run_id:
          type: string
          nullable: true
          description: Originating orchestration run (node producer)
        node_id:
          type: string
          nullable: true
          description: Originating node id within the run's graph
        generation_id:
          type: string
          nullable: true
          description: Originating generation (tool-call producer)
        session_id:
          type: string
          nullable: true
          description: Session the originating generation ran in (tool-call producer)
        agent_id:
          type: string
          nullable: true
          description: Proposing agent
        task_id:
          type: string
          nullable: true
          description: Gated task (task_transition producer)
        task_transition:
          type: string
          nullable: true
          description: Transition fired on approval (task_transition producer)
        policy_version:
          type: string
          nullable: true
        previous_item_id:
          type: string
          nullable: true
          description: Prior item's ID when this proposal was re-filed after an earlier matching item (same
            dedup_key) had been rejected
          example: apr_V1StGXR8Z5jdHi6B
        resolved_by:
          type: string
          nullable: true
          description: Resolving user's public ID; null on expiry
        resolution_reason:
          type: string
          nullable: true
          description: Required on rejection
        edited_arguments:
          type: object
          nullable: true
          description: Set on edit-then-approve
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
  parameters:
    ProjectId:
      name: project_id
      in: path
      required: true
      description: Project public ID (proj_ prefix).
      schema:
        type: string
        example: proj_V1StGXR8Z5jdHi6B
    approval_id:
      name: approval_id
      in: path
      required: true
      description: Approval item ID
      schema:
        type: string
        example: apr_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.
