openapi: 3.1.0
info:
  title: VisIQ Governance API
  version: "1.0.0"
  description: >
    The VisIQ platform API for governing agentic AI. One rule engine evaluates
    every governed agent event against your policies and returns a decision
    (permit, deny, approval-required, redact, mask, or escalate), records a
    cryptographically-verifiable audit trail, and governs agent-to-agent
    delegation.


    All requests are authenticated with a bearer API key. Mint a key in the
    dashboard under **Connectors -> API Keys**; it is presented as
    `Authorization: Bearer vq_prod_...`. Keys are scoped: harness/agent keys
    reach the operational surface (evaluate, rule bundles, telemetry, record
    ingestion), while management keys and dashboard sessions govern rules,
    agents, settings, and audit reads through role-based permissions.


    Decision vocabularies differ per facet. Action governance emits
    `permit | deny | approval_required | mask`. Retrieval governance emits
    `allow | deny | redact | escalate`. The unified `POST /evaluate` surface
    projects both into a single union vocabulary
    (`permit | deny | approval_required | redact | escalate | mask`).
  contact:
    name: VisIQ Labs
    url: https://docs.visiqlabs.com
servers:
  - url: https://api.visiqlabs.com
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Evaluation
    description: Per-event governance decisions for action, retrieval, and delegation.
  - name: Action Rules
    description: Manage the rules that govern agent actions (writes/tool calls).
  - name: Retrieval Rules
    description: Manage the rules that govern retrieval and context exposure.
  - name: Unified Rules
    description: Operation-native rule management across both facets in one surface.
  - name: Rule Bundles
    description: Compiled, cacheable rule bundles the SDK runtime pulls per agent.
  - name: Agents
    description: Register and manage the agents whose events you govern.
  - name: Human-in-the-loop
    description: The approval queue for events that require a human decision.
  - name: Settings
    description: Per-account governance defaults.
  - name: Delegation
    description: Govern scoped agent-to-agent hand-offs with signed grant tokens.
  - name: Records
    description: Ingest, retrieve, finalize, and verify the tamper-evident audit trail.
  - name: Audit Log
    description: Versioned, paginated read APIs over recorded decisions and events.
  - name: Cognition
    description: Read captured Agent Cortex cognition sessions and beats (dedicated cognition:read scope).
paths:
  # ────────────────────────── Evaluation ──────────────────────────
  /allow/evaluate:
    post:
      operationId: evaluateAction
      tags: [Evaluation]
      summary: Evaluate an agent action
      description: >
        Evaluate a single agent action against your action-governance rules and
        return the enforced decision. This is the agent-facing hot path.


        Requires scope `rules:evaluate` (or the legacy `allow:write`).
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ActionEvaluateRequest'
            example:
              agent_id: billing-copilot
              target_app: stripe
              action: refund.create
              context:
                amount_cents: 5000
      responses:
        '200':
          description: The enforced decision.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ActionDecision'
              example:
                decision_id: 4b2b8c1e-6f2a-4a1e-9e2b-9d5b0a1c2d3e
                decision: permit
                reason: Within policy
                rule_code: R-1042
                enforced: true
                agent_mode: enforce
                plane: action
                operation: create
                is_retrieval: false
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /recall/evaluate:
    post:
      operationId: evaluateRetrieval
      tags: [Evaluation]
      summary: Evaluate a retrieval
      description: >
        Evaluate a retrieval, tool call, or prompt render against your
        retrieval-governance rules and return the enforced decision (with mask
        directives when the decision is `redact`).


        Requires scope `rules:evaluate` (or the legacy `recall:write`).
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RetrievalEvaluateRequest'
            example:
              agent_id: support-bot
              operation: retrieve
              resource_type: document
              resource_metadata:
                classification: confidential
              trust_tier: tier2
      responses:
        '200':
          description: The enforced decision.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RetrievalDecision'
              example:
                decision_id: 7c1f2a3b-4d5e-6f70-8192-a3b4c5d6e7f8
                decision: redact
                reason_code: SURFACE_RESTRICTION
                reason: Field-level masking applied
                redaction_rules:
                  - path: ssn
                    strategy: mask
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /evaluate:
    post:
      operationId: evaluateUnified
      tags: [Evaluation]
      summary: Evaluate a governed event (unified)
      description: >
        The single, operation-native evaluation endpoint. Declare which
        operations the event performs via `operations[]` and receive the union
        decision vocabulary. Hybrid events (e.g. `["retrieval","action"]`) are
        evaluated on both facets and combined fail-closed to the most
        restrictive outcome. A legacy `{ kind: "action" | "retrieval", ... }`
        shape is also accepted for compatibility.


        Requires scope `rules:evaluate` (dual-accepts the legacy `allow:write` /
        `recall:write`).
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UnifiedEvaluateRequest'
            example:
              operations: [action]
              agent_id: billing-copilot
              target_app: stripe
              action: refund.create
              context:
                amount_cents: 5000
      responses:
        '200':
          description: The unified decision. `results[]` is present for hybrid events.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnifiedDecision'
              example:
                operations: [action]
                decision: permit
                plane_decision: permit
                reason: Within policy
                reason_code: null
                rule_code: R-1042
                enforced: true
                agent_mode: enforce
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /orchestrate/evaluate:
    post:
      operationId: evaluateDelegation
      tags: [Evaluation, Delegation]
      summary: Enforce a delegated action
      description: >
        A delegated (child) agent checks a single action against the scope of
        its signed grant token. A `deny` outcome is a governance result and is
        still returned with HTTP 200; the hand-off is recorded either way.


        Requires scope `allow:write`.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DelegationEvaluateRequest'
            example:
              grant_token: eyJhbGciOi...
              action: send_email
              resource_type: contacts
      responses:
        '200':
          description: The enforcement decision for this delegated action.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DelegationEvaluateResponse'
              example:
                decision: permit
                reason: Action within grant scope
                handoff_event_id: 9a8b7c6d-5e4f-3210-9876-543210fedcba
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }

  # ────────────────────────── Action Rules ──────────────────────────
  /allow/rules:
    get:
      operationId: listActionRules
      tags: [Action Rules]
      summary: List action rules
      description: 'Paginated list of action-governance rules. Requires permission `allow_rules:view`.'
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit100'
      responses:
        '200':
          description: A page of rules (list rows omit `rego_source`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ActionRuleListPage'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      operationId: createActionRule
      tags: [Action Rules]
      summary: Create an action rule
      description: 'Create an action-governance rule from Rego source. Requires permission `allow_rules:create`.'
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ActionRuleCreate'
            example:
              name: Block large refunds
              description: Require approval for refunds over $500
              rego_source: package visiq.action\n\ndefault decision = "approval_required"
              priority: 50
              enabled: true
      responses:
        '201':
          description: The created rule.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ActionRuleDetail'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }
  /allow/rules/compile:
    post:
      operationId: compileActionRule
      tags: [Action Rules]
      summary: Compile an action rule from natural language
      description: >
        Compile a plain-language policy request into Rego. Rate limited to 10
        requests per minute per account. Set `?stream=true` (or send
        `Accept: text/event-stream`) for a streamed response. When no AI
        provider is configured this facet returns a pre-canned fallback (HTTP
        200 with a `warning`), not an error.


        Requires permission `allow_rules:create`.
      security:
        - bearerAuth: []
      parameters:
        - name: stream
          in: query
          required: false
          schema: { type: boolean }
          description: Stream the compilation as Server-Sent Events.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompileRequest'
            example:
              prompt: Require approval before deleting any customer record
      responses:
        '200':
          description: The compiled rule draft (not yet persisted).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompileResponse'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
  /allow/rules/{id}:
    parameters:
      - $ref: '#/components/parameters/RuleId'
    get:
      operationId: getActionRule
      tags: [Action Rules]
      summary: Get an action rule
      description: 'Fetch a single action rule including its Rego source. Requires permission `allow_rules:view`.'
      security:
        - bearerAuth: []
      responses:
        '200':
          description: The rule.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ActionRuleDetail'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    put:
      operationId: updateActionRule
      tags: [Action Rules]
      summary: Update an action rule
      description: 'Partially update an action rule. At least one field is required. Requires permission `allow_rules:update`.'
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ActionRuleUpdate'
      responses:
        '200':
          description: The updated rule.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ActionRuleDetail'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      operationId: deleteActionRule
      tags: [Action Rules]
      summary: Delete an action rule
      description: 'Delete an action rule. Requires permission `allow_rules:delete`.'
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Deletion confirmation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeletedResponse'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }

  # ────────────────────────── Retrieval Rules ──────────────────────────
  /recall/rules:
    get:
      operationId: listRetrievalRules
      tags: [Retrieval Rules]
      summary: List retrieval rules
      description: 'Paginated list of retrieval-governance rules. Requires permission `recall_rules:view`.'
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit100'
      responses:
        '200':
          description: A page of rules (list rows omit `rego_source`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RetrievalRuleListPage'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      operationId: createRetrievalRule
      tags: [Retrieval Rules]
      summary: Create a retrieval rule
      description: 'Create a retrieval-governance rule from Rego source. Requires permission `recall_rules:create`.'
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RetrievalRuleCreate'
      responses:
        '201':
          description: The created rule.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RetrievalRuleDetail'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /recall/rules/compile:
    post:
      operationId: compileRetrievalRule
      tags: [Retrieval Rules]
      summary: Compile a retrieval rule from natural language
      description: >
        Compile a plain-language retrieval-governance request into Rego. Rate
        limited to 10 requests per minute per account; SSE streaming supported.
        This facet is fail-closed: if no AI provider is configured it returns
        HTTP 503 (unlike action compilation, which returns a fallback).


        Requires permission `recall_rules:create`.
      security:
        - bearerAuth: []
      parameters:
        - name: stream
          in: query
          required: false
          schema: { type: boolean }
          description: Stream the compilation as Server-Sent Events.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompileRequest'
      responses:
        '200':
          description: The compiled rule draft (not yet persisted).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompileResponse'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
        '429': { $ref: '#/components/responses/TooManyRequests' }
        '503': { $ref: '#/components/responses/ServiceUnavailable' }
  /recall/rules/{id}:
    parameters:
      - $ref: '#/components/parameters/RuleId'
    get:
      operationId: getRetrievalRule
      tags: [Retrieval Rules]
      summary: Get a retrieval rule
      description: 'Fetch a single retrieval rule including its Rego source. Requires permission `recall_rules:view`.'
      security:
        - bearerAuth: []
      responses:
        '200':
          description: The rule.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RetrievalRuleDetail'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    put:
      operationId: updateRetrievalRule
      tags: [Retrieval Rules]
      summary: Update a retrieval rule
      description: 'Partially update a retrieval rule. At least one field is required. Requires permission `recall_rules:update`.'
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RetrievalRuleUpdate'
      responses:
        '200':
          description: The updated rule.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RetrievalRuleDetail'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      operationId: deleteRetrievalRule
      tags: [Retrieval Rules]
      summary: Delete a retrieval rule
      description: 'Delete a retrieval rule. Requires permission `recall_rules:delete`.'
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Deletion confirmation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeletedResponse'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /recall/rules/{id}/bypass:
    parameters:
      - $ref: '#/components/parameters/RuleId'
    post:
      operationId: activateRetrievalBypass
      tags: [Retrieval Rules]
      summary: Activate emergency bypass
      description: >
        Temporarily deactivate a single retrieval rule (a time-boxed emergency
        bypass). The activation is written to the audit trail. Requires
        permission `recall_rules:bypass`.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BypassActivate'
            example:
              reason: Incident INC-4821 — investigating a blocked lookup
              duration_minutes: 30
      responses:
        '200':
          description: The rule with its bypass now active.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RetrievalRuleDetail'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
    delete:
      operationId: deactivateRetrievalBypass
      tags: [Retrieval Rules]
      summary: Deactivate emergency bypass
      description: 'Clear an active emergency bypass and re-enforce the rule. Requires permission `recall_rules:bypass`.'
      security:
        - bearerAuth: []
      responses:
        '200':
          description: The rule with its bypass cleared.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RetrievalRuleDetail'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }

  # ────────────────────────── Unified Rules ──────────────────────────
  /rules:
    get:
      operationId: listRules
      tags: [Unified Rules]
      summary: List rules
      description: >
        Paginated list of rules across both facets. Filter by facet with
        `?operations=action,retrieval`. Requires permission `allow_rules:view`.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit100'
        - name: operations
          in: query
          required: false
          description: Comma-separated facet filter; rules overlapping any listed operation are returned.
          schema:
            type: string
            example: action,retrieval
      responses:
        '200':
          description: A page of rules (list rows omit `rego_source`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnifiedRuleListPage'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      operationId: createRule
      tags: [Unified Rules]
      summary: Create a rule
      description: >
        Create a rule tagged with the operations it governs. `operations[]` is
        mapped to `applies_to`. Requires permission `allow_rules:create`.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UnifiedRuleCreate'
            example:
              operations: [action]
              name: Block large refunds
              rego_source: package visiq\n\ndefault decision = "deny"
              target_app: stripe
              priority: 50
      responses:
        '201':
          description: The created rule.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnifiedRuleDetail'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }
  /rules/{id}:
    parameters:
      - $ref: '#/components/parameters/RuleId'
    get:
      operationId: getRule
      tags: [Unified Rules]
      summary: Get a rule
      description: 'Fetch a single rule including its Rego source. Requires permission `allow_rules:view`.'
      security:
        - bearerAuth: []
      responses:
        '200':
          description: The rule.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnifiedRuleDetail'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    put:
      operationId: updateRule
      tags: [Unified Rules]
      summary: Update a rule
      description: 'Partially update a rule. At least one field is required. Requires permission `allow_rules:update`.'
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UnifiedRuleUpdate'
      responses:
        '200':
          description: The updated rule.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnifiedRuleDetail'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
    delete:
      operationId: deleteRule
      tags: [Unified Rules]
      summary: Delete a rule
      description: 'Delete a rule. Requires permission `allow_rules:delete`.'
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Deletion confirmation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeletedResponse'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }

  # ────────────────────────── Rule Bundles ──────────────────────────
  /allow/rules/bundle:
    get:
      operationId: getActionBundle
      tags: [Rule Bundles]
      summary: Get the action rule bundle
      description: >
        The compiled action-governance bundle for one agent, used by the SDK
        runtime. Responds with an `ETag`; send `If-None-Match` to get a `304`.
        A shutdown agent returns a fail-closed shutdown bundle. Requires
        permission `allow_rules:view`.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/AgentIdQuery'
      responses:
        '200':
          description: The compiled bundle.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ActionBundle'
        '304': { description: Not modified (the caller's `If-None-Match` matched the current ETag). }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /recall/rules/bundle:
    get:
      operationId: getRetrievalBundle
      tags: [Rule Bundles]
      summary: Get the retrieval rule bundle
      description: >
        The compiled retrieval-governance bundle for the SDK runtime. Responds
        with an `ETag`; send `If-None-Match` to get a `304`. Requires permission
        `recall_rules:view`.
      security:
        - bearerAuth: []
      responses:
        '200':
          description: The compiled bundle.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RetrievalBundle'
        '304': { description: Not modified. }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /rules/bundle:
    get:
      operationId: getUnifiedBundle
      tags: [Rule Bundles]
      summary: Get the unified rule bundle
      description: >
        The single compiled bundle covering both facets for one agent, carrying
        the resolved agent attributes and dialect version. Responds with an
        `ETag`; send `If-None-Match` to get a `304`. Requires permission
        `allow_rules:view`.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/AgentIdQuery'
      responses:
        '200':
          description: The compiled unified bundle.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnifiedBundle'
        '304': { description: Not modified. }
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }

  # ────────────────────────── Agents ──────────────────────────
  /allow/agents:
    get:
      operationId: listAgents
      tags: [Agents]
      summary: List agents
      description: 'Paginated list of registered agents. Requires permission `allow_agents:view`.'
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit100'
      responses:
        '200':
          description: A page of agents.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentListPage'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      operationId: createAgent
      tags: [Agents]
      summary: Create an agent
      description: >
        Register a new agent. The response includes a one-time plaintext
        `api_key` that is unrecoverable afterwards — store it securely. If you
        omit `api_key` in the request, one is generated. Requires permission
        `allow_agents:create`.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentCreate'
            example:
              agent_id: billing-copilot
              name: Billing Copilot
              mode: enforce
              owner_email: ops@example.com
      responses:
        '201':
          description: The created agent, including the one-time API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentCreateResponse'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }
  /allow/agents/register:
    post:
      operationId: registerAgent
      tags: [Agents]
      summary: Register agent environment
      description: >
        The idempotent registration handshake an SDK/harness performs on
        startup: it provisions the agent if needed and records environment
        metadata (OS, hostname, IP, username). It returns only an acknowledgement
        — no API key. Requires scope `rules:evaluate` (or `allow:write`) and
        permission `allow_agents:create`.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentRegister'
            example:
              agent_id: billing-copilot
              os: linux
              hostname: worker-07
              kind: sdk
      responses:
        '200':
          description: Acknowledgement.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentRegisterResponse'
              example:
                ok: true
                agent_id: billing-copilot
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /allow/agents/{id}:
    parameters:
      - $ref: '#/components/parameters/AgentUuid'
    get:
      operationId: getAgent
      tags: [Agents]
      summary: Get an agent
      description: 'Fetch a single agent by its UUID. Requires permission `allow_agents:view`.'
      security:
        - bearerAuth: []
      responses:
        '200':
          description: The agent.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentDetail'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    put:
      operationId: updateAgent
      tags: [Agents]
      summary: Update an agent
      description: >
        Partially update an agent. Setting `mode` to `null` clears the per-agent
        override so it inherits the account default. At least one field is
        required. Requires permission `allow_agents:update`.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentUpdate'
      responses:
        '200':
          description: The updated agent.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentDetail'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
    delete:
      operationId: deleteAgent
      tags: [Agents]
      summary: Delete an agent
      description: 'Delete an agent. Requires permission `allow_agents:delete`.'
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Deletion confirmation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeletedResponse'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }

  # ────────────────────────── Human-in-the-loop ──────────────────────────
  /allow/hitl/queue:
    get:
      operationId: listHitlQueue
      tags: [Human-in-the-loop]
      summary: List the approval queue
      description: >
        Paginated approval queue. Defaults to `pending` items (FIFO). Filter by
        `status` and `category`. Requires permission `allow_hitl:view`.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit100'
        - name: status
          in: query
          required: false
          schema:
            type: string
            enum: [pending, approved, rejected, timeout, expired, dismissed, resolved, all]
          description: Filter by status. Omit to default to `pending`; use `all` for no filter.
        - name: category
          in: query
          required: false
          schema:
            type: string
            enum: [enduser, engineer, finance, security, compliance, other, all]
          description: Filter by category. `all` (or omitted) applies no category filter.
      responses:
        '200':
          description: A page of queue items.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HitlQueuePage'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    post:
      operationId: createHitlItem
      tags: [Human-in-the-loop]
      summary: Create an approval request
      description: 'Directly enqueue an approval request. Requires permission `allow_hitl:respond`.'
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/HitlCreate'
      responses:
        '201':
          description: The created queue item.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HitlItem'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /allow/hitl/queue/{id}:
    parameters:
      - $ref: '#/components/parameters/HitlId'
    post:
      operationId: respondHitlItem
      tags: [Human-in-the-loop]
      summary: Respond to an approval request
      description: >
        Resolve a pending approval item. The accepted request body depends on
        the item's `category`: `enduser` items take
        `{ decision: "approved" | "rejected", responded_by }`; all other
        categories take `{ action: "dismiss" | "create_rule", responded_by,
        linked_rule_id? }` (`linked_rule_id` is required when `action` is
        `create_rule`). Requires permission `allow_hitl:respond`.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/HitlRespondRequest'
            example:
              action: create_rule
              responded_by: alex@example.com
              linked_rule_id: 4b2b8c1e-6f2a-4a1e-9e2b-9d5b0a1c2d3e
      responses:
        '200':
          description: The resolution outcome.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HitlRespondResponse'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }

  # ────────────────────────── Settings ──────────────────────────
  /allow/settings:
    get:
      operationId: getSettings
      tags: [Settings]
      summary: Get governance settings
      description: >
        Fetch the account's governance defaults. Returns `200` if a row exists,
        or `201` when defaults are first initialized. Requires permission
        `allow_settings:view`.
      security:
        - bearerAuth: []
      responses:
        '200':
          description: The settings.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Settings'
        '201':
          description: Defaults were initialized and returned.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Settings'
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
    put:
      operationId: updateSettings
      tags: [Settings]
      summary: Update governance settings
      description: >
        Partial update of the account's governance defaults. Only the supplied
        fields change; at least one is required. Requires permission
        `allow_settings:update`.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SettingsUpdate'
            example:
              default_agent_mode: enforce
              hitl_timeout_seconds: 90
      responses:
        '200':
          description: The updated settings.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Settings'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }

  # ────────────────────────── Delegation ──────────────────────────
  /orchestrate/grants:
    post:
      operationId: createGrant
      tags: [Delegation]
      summary: Create a delegation grant
      description: >
        A parent agent creates a scoped, time-boxed delegation to a child agent.
        The grant starts `pending` and must be accepted within the accept
        window. Requires scope `allow:write`.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GrantCreate'
            example:
              parent_agent_id: orchestrator
              child_agent_id: emailer
              tool_scope: [send_email]
              duration_seconds: 3600
      responses:
        '200':
          description: The pending grant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GrantCreateResponse'
              example:
                grant_id: 9a8b7c6d-5e4f-3210-9876-543210fedcba
                status: pending
                effective_tool_scope: [send_email]
                effective_context_scope: null
                accept_deadline: '2026-07-16T12:05:00Z'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }
  /orchestrate/grants/{id}:
    parameters:
      - $ref: '#/components/parameters/GrantId'
    get:
      operationId: getGrant
      tags: [Delegation]
      summary: Get grant status
      description: 'Poll a grant''s lifecycle status. Requires scope `allow:write` or `allow:read`.'
      security:
        - bearerAuth: []
      responses:
        '200':
          description: The grant.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Grant'
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /orchestrate/grants/{id}/accept:
    parameters:
      - $ref: '#/components/parameters/GrantId'
    post:
      operationId: acceptGrant
      tags: [Delegation]
      summary: Accept a delegation grant
      description: >
        The child agent accepts a pending grant and receives a signed
        `grant_token` to present on subsequent delegated evaluations. The child
        identity must be supplied in the `X-Agent-ID` header. Requires scope
        `allow:write`.
      security:
        - bearerAuth: []
      parameters:
        - name: X-Agent-ID
          in: header
          required: true
          schema: { type: string }
          description: The accepting child agent's declared identity.
      responses:
        '200':
          description: The signed grant token and its resolved scopes.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GrantAcceptResponse'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
  /orchestrate/grants/{id}/revoke:
    parameters:
      - $ref: '#/components/parameters/GrantId'
    post:
      operationId: revokeGrant
      tags: [Delegation]
      summary: Revoke a delegation grant
      description: >
        Revoke a grant and eagerly cascade the revocation to every descendant
        grant. Returns the number of grants revoked. Requires scope
        `allow:write`.
      security:
        - bearerAuth: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GrantRevokeRequest'
      responses:
        '200':
          description: The number of grants revoked by the cascade.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GrantRevokeResponse'
              example:
                revoked_count: 3
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '422': { $ref: '#/components/responses/UnprocessableEntity' }

  # ────────────────────────── Records ──────────────────────────
  /record/envelopes:
    post:
      operationId: ingestEnvelope
      tags: [Records]
      summary: Ingest a record envelope
      description: >
        Append a signed event (with optional artifacts and attestations) to the
        tamper-evident audit trail. `tenantId` must match the authenticated
        account. Requires permission `record_records:create` and scope
        `record:write`.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RecordEnvelope'
      responses:
        '201':
          description: The created record and event IDs.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EnvelopeIngestResponse'
              example:
                record_id: 4b2b8c1e-6f2a-4a1e-9e2b-9d5b0a1c2d3e
                event_id: 7c1f2a3b-4d5e-6f70-8192-a3b4c5d6e7f8
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /record/envelopes/{id}/verify:
    parameters:
      - $ref: '#/components/parameters/RecordUuid'
    get:
      operationId: verifyEnvelope
      tags: [Records]
      summary: Verify a record envelope
      description: >
        Run the full cryptographic verification chain (leaf signature, Merkle
        inclusion, signed root, and RFC-3161 timestamp) for a record. Requires
        permission `record_records:view` and scope `record:read`.
      security:
        - bearerAuth: []
      responses:
        '200':
          description: The verification result (per-check booleans and details).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VerifyResult'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /record/records:
    get:
      operationId: listRecords
      tags: [Records]
      summary: List records
      description: 'Paginated, filterable list of records. Requires permission `record_records:view`.'
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit200'
        - name: kind
          in: query
          required: false
          schema: { type: string, maxLength: 100 }
        - name: status
          in: query
          required: false
          schema: { type: string, enum: [open, finalized, superseded] }
        - name: correlationId
          in: query
          required: false
          schema: { type: string, maxLength: 255 }
        - name: startDate
          in: query
          required: false
          schema: { type: string, format: date-time }
          description: Inclusive lower bound on `created_at`.
        - name: endDate
          in: query
          required: false
          schema: { type: string, format: date-time }
          description: Inclusive upper bound on `created_at`.
      responses:
        '200':
          description: A page of records.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecordListPage'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /record/records/{id}:
    parameters:
      - $ref: '#/components/parameters/RecordUuid'
    get:
      operationId: getRecord
      tags: [Records]
      summary: Get a record
      description: 'Fetch a single record. Requires permission `record_records:view`.'
      security:
        - bearerAuth: []
      responses:
        '200':
          description: The record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Record'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /record/records/{id}/finalize:
    parameters:
      - $ref: '#/components/parameters/RecordUuid'
    patch:
      operationId: finalizeRecord
      tags: [Records]
      summary: Finalize a record
      description: >
        Seal a record so no further events can be appended. Idempotent — a
        record that is already finalized is returned unchanged. Requires
        permission `record_records:finalize`.
      security:
        - bearerAuth: []
      responses:
        '200':
          description: The finalized record.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Record'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
        '409': { $ref: '#/components/responses/Conflict' }
  /record/records/{id}/events:
    parameters:
      - $ref: '#/components/parameters/RecordUuid'
    get:
      operationId: listRecordEvents
      tags: [Records]
      summary: List record events
      description: 'The ordered event log for a record. Requires permission `record_records:view`.'
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit200'
      responses:
        '200':
          description: A page of events.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecordEventPage'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /record/records/{id}/artifacts:
    parameters:
      - $ref: '#/components/parameters/RecordUuid'
    get:
      operationId: listRecordArtifacts
      tags: [Records]
      summary: List record artifacts
      description: 'The artifacts attached to a record. Requires permission `record_records:view`.'
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit200'
      responses:
        '200':
          description: A page of artifacts.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecordArtifactPage'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /record/records/{id}/attestations:
    parameters:
      - $ref: '#/components/parameters/RecordUuid'
    get:
      operationId: listRecordAttestations
      tags: [Records]
      summary: List record attestations
      description: 'The attestations attached to a record. Requires permission `record_records:view`.'
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit200'
      responses:
        '200':
          description: A page of attestations.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecordAttestationPage'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /record/checkpoints:
    get:
      operationId: listCheckpoints
      tags: [Records]
      summary: List transparency-log checkpoints
      description: >
        The tenant-neutral transparency-log checkpoint feed (signed Merkle roots
        and counts). Paginated by sequence cursor. Requires permission
        `record_records:view` and scope `record:read`.
      security:
        - bearerAuth: []
      parameters:
        - name: before
          in: query
          required: false
          schema: { type: integer, minimum: 1 }
          description: Return checkpoints with a batch sequence below this cursor.
        - name: limit
          in: query
          required: false
          schema: { type: integer, minimum: 1 }
          description: Page size (bounded by a server maximum).
        - name: domain
          in: query
          required: false
          schema: { type: string, enum: [production, sandbox], default: production }
      responses:
        '200':
          description: A page of checkpoints.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CheckpointPage'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /record/checkpoints/{seq}:
    parameters:
      - name: seq
        in: path
        required: true
        schema: { type: integer, minimum: 1 }
        description: The checkpoint batch sequence number.
    get:
      operationId: getCheckpoint
      tags: [Records]
      summary: Get a checkpoint
      description: 'Fetch a single checkpoint by its batch sequence. Requires permission `record_records:view` and scope `record:read`.'
      security:
        - bearerAuth: []
      parameters:
        - name: domain
          in: query
          required: false
          schema: { type: string, enum: [production, sandbox], default: production }
      responses:
        '200':
          description: The checkpoint.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Checkpoint'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /record/chain/consistency:
    get:
      operationId: getChainConsistency
      tags: [Records]
      summary: Verify hash-chain consistency
      description: >
        Return a consistency proof over the checkpoint hash-chain for a
        sequence range. An unverifiable result is reported as `verified: false`
        in a 200 body (fail-closed), not an error. Requires permission
        `record_records:view` and scope `record:read`.
      security:
        - bearerAuth: []
      parameters:
        - name: from
          in: query
          required: false
          schema: { type: integer, minimum: 1 }
        - name: to
          in: query
          required: false
          schema: { type: integer, minimum: 1 }
        - name: live_kms
          in: query
          required: false
          schema: { type: boolean }
          description: Verify the signed root against the live KMS key rather than cached material.
      responses:
        '200':
          description: The consistency proof.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConsistencyResult'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }

  # ────────────────────────── Audit Log (versioned) ──────────────────────────
  /v1/allow/audit-log:
    get:
      operationId: getActionAuditLog
      tags: [Audit Log]
      summary: Query the action decision log
      description: >
        Paginated, filterable log of action-governance decisions. Requires
        permission `allow_audit_log:view` and scope `allow:read`.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit100'
        - { name: agent_id, in: query, required: false, schema: { type: string, maxLength: 255 } }
        - { name: target_app, in: query, required: false, schema: { type: string, maxLength: 255 } }
        - name: decision
          in: query
          required: false
          schema: { type: string, enum: [permit, deny, approval_required, mask] }
        - name: hitl_result
          in: query
          required: false
          schema: { type: string, enum: [approved, rejected, timeout] }
        - { name: start_date, in: query, required: false, schema: { type: string, format: date-time } }
        - { name: end_date, in: query, required: false, schema: { type: string, format: date-time } }
      responses:
        '200':
          description: A page of decision entries.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ActionAuditLogPage'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /v1/allow/decisions/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema: { type: string, format: uuid }
        description: The decision (audit-log entry) UUID.
    get:
      operationId: getActionDecision
      tags: [Audit Log]
      summary: Get a single action decision
      description: >
        Fetch one action decision by ID, including the linked approval item when
        the decision was `approval_required`. Requires permission
        `allow_audit_log:view` and scope `rules:read` (or `allow:read`).
      security:
        - bearerAuth: []
      responses:
        '200':
          description: The decision, with an attached `hitl` object (or `null`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DecisionDetail'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }
  /v1/recall/audit-log:
    get:
      operationId: getRetrievalAuditLog
      tags: [Audit Log]
      summary: Query the retrieval decision log
      description: >
        Paginated, filterable log of retrieval-governance decisions. Requires
        permission `recall_audit_log:view` and scope `recall:read`.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit100'
        - { name: agent_id, in: query, required: false, schema: { type: string, maxLength: 255 } }
        - name: operation
          in: query
          required: false
          schema:
            type: string
            enum: [retrieve, tool_call, prompt_render, emergency_bypass_activate, emergency_bypass_deactivate]
        - name: decision
          in: query
          required: false
          schema: { type: string, enum: [allow, deny, redact, escalate] }
        - name: reason_code
          in: query
          required: false
          schema:
            type: string
            enum: [TIER_MISMATCH, PRINCIPAL_EXCLUDED, POLICY_DENY, AUDIENCE_EXPANSION, SURFACE_RESTRICTION, POLICY_ALLOW, DEFAULT_DENY, EMERGENCY_BYPASS]
        - { name: start_date, in: query, required: false, schema: { type: string, format: date-time } }
        - { name: end_date, in: query, required: false, schema: { type: string, format: date-time } }
      responses:
        '200':
          description: A page of decision entries.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RetrievalAuditLogPage'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /v1/record/audit-log:
    get:
      operationId: getRecordAuditLog
      tags: [Audit Log]
      summary: Query the record event log
      description: >
        Paginated, filterable log of recorded events across all records.
        Requires permission `record_audit_log:view` and scope `record:read`.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit200'
        - name: source
          in: query
          required: false
          schema:
            type: string
            enum: [execute, browser_proxy, session_events, action, isolate, app, retrieval]
        - { name: correlation_id, in: query, required: false, schema: { type: string, maxLength: 255 } }
        - { name: event_type, in: query, required: false, schema: { type: string, maxLength: 255 } }
        - { name: start_date, in: query, required: false, schema: { type: string, format: date-time } }
        - { name: end_date, in: query, required: false, schema: { type: string, format: date-time } }
      responses:
        '200':
          description: A page of event entries.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecordAuditLogPage'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }

  # ──────────────────── Agent Cortex cognition (versioned) ───────────────────
  /v1/allow/cognition/sessions:
    get:
      operationId: getCognitionSessions
      tags: [Cognition]
      summary: Query captured cognition sessions
      description: >
        Paginated list of Agent Cortex cognition sessions (capture is
        default-OFF and server-gated per agent). Sorted by `last_event_at`
        descending. Sessions are identified by their natural `session_id` key.
        Requires permission `allow_cognition:view` and the dedicated scope
        `cognition:read` (never granted by `allow:read`/`rules:read`).
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit100'
        - name: agent_id
          in: query
          required: false
          schema: { type: string, maxLength: 255 }
          description: Filter on the session's root agent id.
        - { name: framework, in: query, required: false, schema: { type: string, maxLength: 64 } }
        - { name: start_date, in: query, required: false, schema: { type: string, format: date-time } }
        - { name: end_date, in: query, required: false, schema: { type: string, format: date-time } }
      responses:
        '200':
          description: A page of cognition sessions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CognitionSessionPage'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
  /v1/allow/cognition/events:
    get:
      operationId: getCognitionEvents
      tags: [Cognition]
      summary: Query cognition beats (events)
      description: >
        Paginated cognition beats. Under a `session_id` filter, beats are
        ordered `seq` ascending (the client-authoritative interleave order);
        otherwise `created_at` descending. `limit` above 200 is a 400, never a
        silent clamp. Stored content is floor-redacted; the encrypted raw is
        NEVER served here (reveal is the audited unmask endpoint). Beat `cost`
        carries raw token buckets only — price at read time. Requires
        permission `allow_cognition:view` and scope `cognition:read`.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit200'
        - { name: session_id, in: query, required: false, schema: { type: string, maxLength: 255 } }
        - { name: agent_id, in: query, required: false, schema: { type: string, maxLength: 255 } }
        - name: kind
          in: query
          required: false
          schema:
            type: string
            enum: [thought, speech, action, observation, intervention, spawn, phase, handoff, lifecycle, error, memory]
        - { name: thread_id, in: query, required: false, schema: { type: string, maxLength: 255 } }
        - { name: start_date, in: query, required: false, schema: { type: string, format: date-time } }
        - { name: end_date, in: query, required: false, schema: { type: string, format: date-time } }
      responses:
        '200':
          description: A page of cognition beats.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CognitionEventPage'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >
        A VisIQ API key presented as `Authorization: Bearer vq_prod_...`. Mint
        keys under **Connectors -> API Keys** in the dashboard.
  parameters:
    Page:
      name: page
      in: query
      required: false
      schema: { type: integer, minimum: 1, default: 1 }
      description: 1-based page number.
    Limit100:
      name: limit
      in: query
      required: false
      schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
      description: Page size (max 100).
    Limit200:
      name: limit
      in: query
      required: false
      schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
      description: Page size (max 200).
    RuleId:
      name: id
      in: path
      required: true
      schema: { type: string, format: uuid }
      description: The rule UUID.
    AgentUuid:
      name: id
      in: path
      required: true
      schema: { type: string, format: uuid }
      description: The agent's row UUID (not the human-readable agent_id).
    HitlId:
      name: id
      in: path
      required: true
      schema: { type: string, format: uuid }
      description: The approval queue item UUID.
    GrantId:
      name: id
      in: path
      required: true
      schema: { type: string }
      description: The delegation grant ID.
    RecordUuid:
      name: id
      in: path
      required: true
      schema: { type: string, format: uuid }
      description: The record UUID.
    AgentIdQuery:
      name: agent_id
      in: query
      required: true
      schema: { type: string, minLength: 1, maxLength: 255 }
      description: The human-readable agent identifier the bundle is compiled for.
  responses:
    BadRequest:
      description: The request was malformed or failed validation.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
          example:
            error: Invalid request body
            details:
              - path: [agent_id]
                message: Required
    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
          example:
            error: Unauthorized
    Forbidden:
      description: The key lacks the required permission or scope.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
          example:
            error: insufficient_scope
            detail: This API key is not authorized for the requested operation.
    NotFound:
      description: The requested resource does not exist (or is not visible to this account).
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
          example:
            error: Rule not found
    Conflict:
      description: The request conflicts with current state.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    UnprocessableEntity:
      description: The request was well-formed but could not be fulfilled.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    TooManyRequests:
      description: Rate limit exceeded.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
          example:
            error: Rate limit exceeded. Maximum 10 compile requests per minute.
    ServiceUnavailable:
      description: A dependency required to serve the request is not configured.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
  schemas:
    Error:
      type: object
      required: [error]
      description: >
        The standard error envelope. `details` carries the validation issue list
        on 400s; authorization failures add fields such as `detail`,
        `requiredScopes`, or `requiredPermission`.
      properties:
        error: { type: string }
        detail: { type: string }
        code: { type: string }
        details:
          type: array
          items:
            type: object
            additionalProperties: true
      additionalProperties: true
    DeletedResponse:
      type: object
      required: [deleted]
      properties:
        deleted: { type: boolean, const: true }

    # ── Evaluation ──
    ActionEvaluateRequest:
      type: object
      required: [agent_id, target_app, action]
      properties:
        agent_id: { type: string, minLength: 1, maxLength: 255 }
        target_app: { type: string, minLength: 1, maxLength: 255 }
        action: { type: string, minLength: 1, maxLength: 255 }
        context:
          type: object
          additionalProperties: true
          default: {}
          description: Arbitrary decision inputs (e.g. amount, resource id).
        session_id: { type: string, minLength: 1, maxLength: 255 }
        telemetry:
          type: object
          additionalProperties: true
    ActionDecision:
      type: object
      required: [decision_id, decision, reason, enforced, agent_mode]
      properties:
        decision_id: { type: string, format: uuid }
        decision:
          type: string
          enum: [permit, deny, approval_required, mask]
        reason: { type: string }
        rule_code:
          type: [string, "null"]
          description: The matched rule code (synthetic `D-*` for defaults; null in off-mode).
        enforced: { type: boolean }
        agent_mode: { type: string, enum: [off, monitor, enforce] }
        plane:
          type: [string, "null"]
          enum: [action, retrieval, delegation, null]
        operation: { type: [string, "null"] }
        is_retrieval: { type: boolean }
        arg_redaction_rules:
          type: array
          description: Present only when `decision` is `mask`.
          items: { type: object, additionalProperties: true }
        hitl_fallback:
          type: string
          enum: [mask, deny]
          description: Present only when `decision` is `approval_required` — the fallback if no human responds.
    RetrievalEvaluateRequest:
      type: object
      required: [agent_id, operation]
      properties:
        agent_id: { type: string, minLength: 1, maxLength: 255 }
        operation: { type: string, enum: [retrieve, tool_call, prompt_render] }
        resource_type: { type: string, minLength: 1, maxLength: 255, default: document }
        resource_metadata:
          type: object
          additionalProperties: true
          default: {}
        trust_tier: { type: string, maxLength: 50 }
        surface: { type: string, maxLength: 100 }
        query: { type: string, maxLength: 2000 }
        session_id: { type: string, minLength: 1, maxLength: 255 }
        telemetry:
          type: object
          additionalProperties: true
    RetrievalDecision:
      type: object
      required: [decision_id, decision, reason_code, reason]
      properties:
        decision_id: { type: string, format: uuid }
        decision:
          type: string
          enum: [allow, deny, redact, escalate]
          description: Monitor mode normalizes any non-`allow` engine outcome to `allow`.
        reason_code: { type: string }
        reason: { type: string }
        redaction_rules:
          type: array
          description: Present only when `decision` is `redact` — the mask directives to apply.
          items: { type: object, additionalProperties: true }
        hitl_fallback:
          type: string
          enum: [mask]
          description: Present only when `decision` is `escalate`.
    UnifiedEvaluateRequest:
      type: object
      required: [operations, agent_id]
      description: >
        The operation-native request. Include action fields (`target_app`,
        `action`) when `operations` contains `action` or `delegation`; include
        retrieval fields (`resource_type`, ...) when it contains `retrieval`. A
        legacy `{ kind: "action" | "retrieval", ... }` body is also accepted.
      properties:
        operations:
          type: array
          minItems: 1
          maxItems: 3
          items: { type: string, enum: [action, retrieval, delegation] }
        agent_id: { type: string, minLength: 1, maxLength: 255 }
        target_app: { type: string, minLength: 1, maxLength: 255 }
        action: { type: string, minLength: 1, maxLength: 255 }
        context:
          type: object
          additionalProperties: true
          default: {}
        operation: { type: string, enum: [retrieve, tool_call, prompt_render] }
        resource_type: { type: string, minLength: 1, maxLength: 255 }
        resource_metadata:
          type: object
          additionalProperties: true
          default: {}
        trust_tier: { type: [string, "null"], maxLength: 64 }
        surface: { type: [string, "null"], maxLength: 128 }
        query: { type: [string, "null"], maxLength: 4000 }
        telemetry:
          type: object
          additionalProperties: true
    UnifiedDecision:
      type: object
      required: [operations, decision]
      description: >
        For single-facet events the top-level fields carry the projected
        decision. For hybrid events, `decision` is the most-restrictive outcome
        and `results[]` carries the per-operation breakdown.
      properties:
        operations:
          type: array
          items: { type: string, enum: [action, retrieval, delegation] }
        decision:
          type: string
          enum: [permit, deny, approval_required, redact, escalate, mask]
        plane_decision:
          type: string
          description: The originating facet's verbatim decision word (single-facet responses).
        reason: { type: [string, "null"] }
        reason_code: { type: [string, "null"] }
        rule_code: { type: [string, "null"] }
        enforced: { type: [boolean, "null"] }
        agent_mode: { type: [string, "null"], enum: [off, monitor, enforce, null] }
        decision_id: { type: [string, "null"] }
        redaction_rules:
          type: array
          items: { type: object, additionalProperties: true }
        hitl_fallback: { type: string }
        results:
          type: array
          description: Present for hybrid events — one entry per evaluated operation.
          items:
            type: object
            properties:
              operation: { type: string, enum: [action, retrieval, delegation] }
              decision:
                type: string
                enum: [permit, deny, approval_required, redact, escalate, mask]
              plane_decision: { type: string }
              reason: { type: [string, "null"] }
              reason_code: { type: [string, "null"] }
              rule_code: { type: [string, "null"] }
    DelegationEvaluateRequest:
      type: object
      required: [grant_token, action]
      properties:
        grant_token: { type: string, minLength: 1, description: The signed grant token issued at accept time. }
        action: { type: string, minLength: 1, maxLength: 255 }
        resource_type: { type: [string, "null"], maxLength: 255 }
        resource_metadata:
          type: object
          additionalProperties: true
          default: {}
    DelegationEvaluateResponse:
      type: object
      required: [decision, reason, handoff_event_id]
      properties:
        decision: { type: string, enum: [permit, deny] }
        reason: { type: string }
        handoff_event_id: { type: string }

    # ── Rules (shared) ──
    CompileRequest:
      type: object
      required: [prompt]
      properties:
        prompt: { type: string, minLength: 1, maxLength: 4000 }
        nodeRef: { type: string, maxLength: 255 }
    CompileResponse:
      type: object
      required: [rego_source, natural_language, name, description, suggested_priority]
      properties:
        rego_source: { type: string }
        natural_language: { type: string }
        name: { type: string }
        description: { type: string }
        suggested_priority: { type: integer }
        warning:
          type: string
          description: Present on the action-facet pre-canned fallback when no AI provider is configured.
    ActionRuleCreate:
      type: object
      required: [name, rego_source]
      properties:
        name: { type: string, minLength: 1, maxLength: 255 }
        description: { type: string, maxLength: 1000 }
        rego_source: { type: string, minLength: 1 }
        natural_language: { type: string, maxLength: 2000 }
        priority: { type: integer, default: 0 }
        enabled: { type: boolean, default: true }
    ActionRuleUpdate:
      type: object
      minProperties: 1
      properties:
        name: { type: string, minLength: 1, maxLength: 255 }
        description: { type: string, maxLength: 1000 }
        rego_source: { type: string, minLength: 1 }
        natural_language: { type: string, maxLength: 2000 }
        priority: { type: integer }
        enabled: { type: boolean }
    ActionRuleDetail:
      type: object
      properties:
        id: { type: string, format: uuid }
        name: { type: string }
        description: { type: [string, "null"] }
        rego_source: { type: string }
        natural_language: { type: [string, "null"] }
        priority: { type: integer }
        enabled: { type: boolean }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    ActionRuleListItem:
      type: object
      description: A list row — identical to the detail object minus `rego_source`.
      properties:
        id: { type: string, format: uuid }
        name: { type: string }
        description: { type: [string, "null"] }
        natural_language: { type: [string, "null"] }
        priority: { type: integer }
        enabled: { type: boolean }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    ActionRuleListPage:
      allOf:
        - $ref: '#/components/schemas/PageEnvelope'
        - type: object
          properties:
            data:
              type: array
              items: { $ref: '#/components/schemas/ActionRuleListItem' }
    RetrievalRuleCreate:
      type: object
      required: [name, rego_source]
      properties:
        name: { type: string, minLength: 1, maxLength: 255 }
        description: { type: string, maxLength: 1000 }
        rego_source: { type: string, minLength: 1 }
        natural_language: { type: string, maxLength: 2000 }
        priority: { type: integer, default: 0 }
        enabled: { type: boolean, default: true }
        trust_tier: { type: string, maxLength: 50 }
        surface: { type: string, maxLength: 100 }
        principal_exclusions:
          type: array
          items: { type: string, minLength: 1, maxLength: 255 }
    RetrievalRuleUpdate:
      type: object
      minProperties: 1
      properties:
        name: { type: string, minLength: 1, maxLength: 255 }
        description: { type: string, maxLength: 1000 }
        rego_source: { type: string, minLength: 1 }
        natural_language: { type: string, maxLength: 2000 }
        priority: { type: integer }
        enabled: { type: boolean }
        trust_tier: { type: [string, "null"], maxLength: 50 }
        surface: { type: [string, "null"], maxLength: 100 }
        principal_exclusions:
          type: [array, "null"]
          items: { type: string, minLength: 1, maxLength: 255 }
    RetrievalRuleDetail:
      type: object
      properties:
        id: { type: string, format: uuid }
        name: { type: string }
        description: { type: [string, "null"] }
        rego_source: { type: string }
        natural_language: { type: [string, "null"] }
        priority: { type: integer }
        enabled: { type: boolean }
        trust_tier: { type: [string, "null"] }
        surface: { type: [string, "null"] }
        principal_exclusions:
          type: [array, "null"]
          items: { type: string }
        bypass_active: { type: boolean }
        bypass_reason: { type: [string, "null"] }
        bypass_activated_by: { type: [string, "null"] }
        bypass_activated_at: { type: [string, "null"], format: date-time }
        bypass_expires_at: { type: [string, "null"], format: date-time }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    RetrievalRuleListItem:
      type: object
      description: A list row — the detail object minus `rego_source`.
      properties:
        id: { type: string, format: uuid }
        name: { type: string }
        description: { type: [string, "null"] }
        natural_language: { type: [string, "null"] }
        priority: { type: integer }
        enabled: { type: boolean }
        trust_tier: { type: [string, "null"] }
        surface: { type: [string, "null"] }
        principal_exclusions:
          type: [array, "null"]
          items: { type: string }
        bypass_active: { type: boolean }
        bypass_reason: { type: [string, "null"] }
        bypass_activated_by: { type: [string, "null"] }
        bypass_activated_at: { type: [string, "null"], format: date-time }
        bypass_expires_at: { type: [string, "null"], format: date-time }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    RetrievalRuleListPage:
      allOf:
        - $ref: '#/components/schemas/PageEnvelope'
        - type: object
          properties:
            data:
              type: array
              items: { $ref: '#/components/schemas/RetrievalRuleListItem' }
    BypassActivate:
      type: object
      required: [reason, duration_minutes]
      properties:
        reason: { type: string, minLength: 10, description: Justification (at least 10 characters). }
        duration_minutes:
          type: integer
          enum: [15, 30, 60, 120]
          description: How long the bypass stays active.
    UnifiedRuleCreate:
      type: object
      required: [operations, name, rego_source]
      properties:
        operations:
          type: array
          minItems: 1
          maxItems: 3
          items: { type: string, enum: [action, retrieval, delegation] }
        name: { type: string, minLength: 1, maxLength: 255 }
        description: { type: string, maxLength: 1000 }
        rego_source: { type: string, minLength: 1, maxLength: 100000 }
        natural_language: { type: string, maxLength: 2000 }
        priority: { type: integer, default: 0 }
        enabled: { type: boolean, default: true }
        target_app: { type: [string, "null"], maxLength: 255 }
        action_pattern: { type: [string, "null"], maxLength: 255 }
        trust_tier: { type: [string, "null"], maxLength: 64 }
        surface: { type: [string, "null"], maxLength: 128 }
        principal_exclusions:
          type: [array, "null"]
          items: { type: string, minLength: 1, maxLength: 255 }
    UnifiedRuleUpdate:
      type: object
      minProperties: 1
      properties:
        operations:
          type: array
          minItems: 1
          maxItems: 3
          items: { type: string, enum: [action, retrieval, delegation] }
        name: { type: string, minLength: 1, maxLength: 255 }
        description: { type: string, maxLength: 1000 }
        rego_source: { type: string, minLength: 1, maxLength: 100000 }
        natural_language: { type: string, maxLength: 2000 }
        priority: { type: integer }
        enabled: { type: boolean }
        target_app: { type: [string, "null"], maxLength: 255 }
        action_pattern: { type: [string, "null"], maxLength: 255 }
        trust_tier: { type: [string, "null"], maxLength: 64 }
        surface: { type: [string, "null"], maxLength: 128 }
        principal_exclusions:
          type: [array, "null"]
          items: { type: string, minLength: 1, maxLength: 255 }
    UnifiedRuleDetail:
      type: object
      properties:
        id: { type: string, format: uuid }
        rule_code: { type: string }
        name: { type: string }
        description: { type: [string, "null"] }
        rego_source: { type: string }
        natural_language: { type: [string, "null"] }
        priority: { type: integer }
        enabled: { type: boolean }
        applies_to:
          type: array
          items: { type: string, enum: [action, retrieval, delegation] }
        target_app: { type: [string, "null"] }
        action_pattern: { type: [string, "null"] }
        trust_tier: { type: [string, "null"] }
        surface: { type: [string, "null"] }
        principal_exclusions:
          type: [array, "null"]
          items: { type: string }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    UnifiedRuleListItem:
      type: object
      description: A list row — the detail object minus `rego_source`.
      properties:
        id: { type: string, format: uuid }
        rule_code: { type: string }
        name: { type: string }
        description: { type: [string, "null"] }
        natural_language: { type: [string, "null"] }
        priority: { type: integer }
        enabled: { type: boolean }
        applies_to:
          type: array
          items: { type: string, enum: [action, retrieval, delegation] }
        target_app: { type: [string, "null"] }
        action_pattern: { type: [string, "null"] }
        trust_tier: { type: [string, "null"] }
        surface: { type: [string, "null"] }
        principal_exclusions:
          type: [array, "null"]
          items: { type: string }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    UnifiedRuleListPage:
      allOf:
        - $ref: '#/components/schemas/PageEnvelope'
        - type: object
          properties:
            data:
              type: array
              items: { $ref: '#/components/schemas/UnifiedRuleListItem' }

    # ── Bundles ──
    NoCoverageBlock:
      type: object
      properties:
        no_coverage_defaults:
          type: object
          properties:
            read: { type: string }
            write: { type: string }
            delete: { type: string }
            admin: { type: string }
        autopilot_enabled: { type: boolean }
        enduser_hitl_enabled: { type: boolean }
        hitl_timeout_seconds: { type: integer }
    ActionBundle:
      type: object
      description: >
        The compiled action bundle. A shutdown agent returns `version:
        "shutdown"`, `shutdown: true`, empty `rules`, and fail-closed defaults.
      properties:
        version: { type: string, description: Content hash (also the ETag). }
        agent_mode: { type: string, enum: [enforce, monitor, off] }
        agent_mode_by_operation:
          type: object
          additionalProperties: { type: string, enum: [enforce, monitor, off] }
          description: Present only when non-empty.
        shutdown: { type: boolean }
        rules:
          type: array
          items: { $ref: '#/components/schemas/BundleRule' }
        no_coverage: { $ref: '#/components/schemas/NoCoverageBlock' }
    BundleRule:
      type: object
      properties:
        id: { type: string }
        rule_code: { type: string }
        name: { type: string }
        description: { type: [string, "null"] }
        effect: { type: string }
        rego_source: { type: string }
        resource_type: { type: string }
        target_app: { type: [string, "null"] }
        action_pattern: { type: [string, "null"] }
        conditions:
          type: array
          items:
            type: object
            properties:
              field: { type: string }
              operator: { type: string }
              value: {}
        priority: { type: integer }
    RetrievalBundle:
      type: object
      properties:
        version: { type: string }
        rules:
          type: array
          items:
            type: object
            properties:
              id: { type: string }
              name: { type: string }
              rego_source: { type: string }
              trust_tier: { type: [string, "null"] }
              surface: { type: [string, "null"] }
              principal_exclusions:
                type: array
                items: { type: string }
              priority: { type: integer }
    UnifiedBundle:
      type: object
      description: >
        The unified bundle carries the dialect version, resolved agent
        attributes, and the raw effective-rule projection. The shutdown variant
        matches the action bundle's shutdown shape.
      properties:
        version: { type: string }
        dialect_version: { type: integer }
        min_dialect: { type: integer }
        agent_mode: { type: string, enum: [enforce, monitor, off] }
        agent_mode_by_operation:
          type: object
          additionalProperties: { type: string, enum: [enforce, monitor, off] }
        cognition_capture: { type: boolean }
        agent_attributes:
          type: object
          properties:
            trust_tier: { type: [string, "null"] }
            categories: {}
            business_functions:
              type: array
              items: { type: string }
            business_function: { type: [string, "null"] }
            blast_radius_tier: { type: [string, "null"] }
            no_coverage: {}
        shutdown: { type: boolean }
        rules:
          type: array
          items: { type: object, additionalProperties: true }
        no_coverage: { $ref: '#/components/schemas/NoCoverageBlock' }

    # ── Agents ──
    AgentModeByOperation:
      type: object
      description: Per-operation mode overrides (any subset of the three keys).
      properties:
        action: { type: string, enum: [enforce, monitor, off] }
        retrieval: { type: string, enum: [enforce, monitor, off] }
        delegation: { type: string, enum: [enforce, monitor, off] }
      additionalProperties: false
    AgentCreate:
      type: object
      required: [agent_id, name]
      properties:
        agent_id: { type: string, minLength: 1, maxLength: 255 }
        name: { type: string, minLength: 1, maxLength: 255 }
        description: { type: string, maxLength: 1000 }
        mode: { type: [string, "null"], enum: [enforce, monitor, off, null] }
        mode_by_operation:
          oneOf:
            - $ref: '#/components/schemas/AgentModeByOperation'
            - type: "null"
        api_key:
          type: string
          minLength: 16
          maxLength: 256
          description: Optional caller-supplied key; if omitted, one is generated and returned once.
        owner_email: { type: string, format: email, maxLength: 320 }
        agent_type:
          type: string
          enum: [langchain, openclaw, llamaindex, autogen, crewai, semantic_kernel, vercel_ai, mastra, voltagent, openai_agents, unknown]
        hitl_pathway: { type: string, enum: [slack, teams, email] }
        trust_tier: { type: string, description: A trust-tier identifier constrained server-side. }
        categories:
          type: array
          items: { type: string }
          description: Agent categories constrained server-side.
        no_coverage: { type: [string, "null"], enum: [open, closed, null] }
    AgentUpdate:
      type: object
      minProperties: 1
      properties:
        name: { type: string, minLength: 1, maxLength: 255 }
        description: { type: string, maxLength: 1000 }
        mode: { type: [string, "null"], enum: [enforce, monitor, off, null] }
        mode_by_operation:
          oneOf:
            - $ref: '#/components/schemas/AgentModeByOperation'
            - type: "null"
        owner_email: { type: [string, "null"], format: email, maxLength: 320 }
        hitl_pathway: { type: [string, "null"], enum: [slack, teams, email, null] }
        trust_tier: { type: [string, "null"] }
        categories:
          type: array
          items: { type: string }
        business_function: { type: [string, "null"], description: One of the closed business-function ids. }
        business_functions:
          type: array
          items: { type: string }
        business_function_source: { type: string, enum: [ai, user] }
        no_coverage: { type: [string, "null"], enum: [open, closed, null] }
    AgentRegister:
      type: object
      required: [agent_id]
      properties:
        agent_id: { type: string, minLength: 1, maxLength: 255 }
        os: { type: string, maxLength: 255 }
        hostname: { type: string, maxLength: 255 }
        ip: { type: string, maxLength: 255 }
        username: { type: string, maxLength: 255 }
        kind: { type: string, enum: [sdk, cli_harness] }
        agent_type:
          type: string
          enum: [langchain, openclaw, llamaindex, autogen, crewai, semantic_kernel, vercel_ai, mastra, voltagent, openai_agents, unknown]
    AgentRegisterResponse:
      type: object
      required: [ok, agent_id]
      properties:
        ok: { type: boolean }
        agent_id: { type: string }
    AgentDetail:
      type: object
      properties:
        id: { type: string, format: uuid }
        agent_id: { type: string }
        name: { type: string }
        description: { type: [string, "null"] }
        mode: { type: [string, "null"], enum: [enforce, monitor, off, null] }
        mode_by_operation:
          oneOf:
            - $ref: '#/components/schemas/AgentModeByOperation'
            - type: "null"
        owner_email: { type: [string, "null"] }
        hitl_pathway: { type: [string, "null"] }
        agent_os: { type: [string, "null"] }
        agent_hostname: { type: [string, "null"] }
        agent_ip: { type: [string, "null"] }
        agent_username: { type: [string, "null"] }
        trust_tier: { type: [string, "null"] }
        categories:
          type: [array, "null"]
          items: { type: string }
        business_function: { type: [string, "null"] }
        business_functions:
          type: [array, "null"]
          items: { type: string }
        business_function_source: { type: [string, "null"] }
        no_coverage: { type: [string, "null"] }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    AgentListItem:
      type: object
      description: A list row — the detail object minus the environment columns.
      properties:
        id: { type: string, format: uuid }
        agent_id: { type: string }
        name: { type: string }
        description: { type: [string, "null"] }
        mode: { type: [string, "null"], enum: [enforce, monitor, off, null] }
        mode_by_operation:
          oneOf:
            - $ref: '#/components/schemas/AgentModeByOperation'
            - type: "null"
        owner_email: { type: [string, "null"] }
        hitl_pathway: { type: [string, "null"] }
        trust_tier: { type: [string, "null"] }
        categories:
          type: [array, "null"]
          items: { type: string }
        business_function: { type: [string, "null"] }
        business_functions:
          type: [array, "null"]
          items: { type: string }
        business_function_source: { type: [string, "null"] }
        no_coverage: { type: [string, "null"] }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    AgentCreateResponse:
      allOf:
        - $ref: '#/components/schemas/AgentDetail'
        - type: object
          required: [api_key]
          properties:
            api_key:
              type: string
              description: The one-time plaintext key. Store it now — it cannot be retrieved again.
    AgentListPage:
      allOf:
        - $ref: '#/components/schemas/PageEnvelope'
        - type: object
          properties:
            data:
              type: array
              items: { $ref: '#/components/schemas/AgentListItem' }

    # ── HITL ──
    HitlItem:
      type: object
      properties:
        id: { type: string, format: uuid }
        decision_id: { type: [string, "null"], format: uuid }
        agent_id: { type: string }
        target_app: { type: string }
        action: { type: string }
        context: { type: object, additionalProperties: true }
        category: { type: string, enum: [enduser, engineer, finance, security, compliance, other] }
        status: { type: string, enum: [pending, approved, rejected, timeout, expired, dismissed, resolved] }
        ai_recommended_rule: { type: [object, "null"], additionalProperties: true }
        notified_via: { type: [array, "null"], items: { type: string } }
        expires_at: { type: [string, "null"], format: date-time }
        responded_at: { type: [string, "null"], format: date-time }
        responded_by: { type: [string, "null"] }
        created_at: { type: string, format: date-time }
    HitlQueuePage:
      allOf:
        - $ref: '#/components/schemas/PageEnvelope'
        - type: object
          properties:
            data:
              type: array
              items: { $ref: '#/components/schemas/HitlItem' }
    HitlCreate:
      type: object
      required: [agent_id, action]
      properties:
        agent_id: { type: string, minLength: 1, maxLength: 255 }
        target_app: { type: string, minLength: 1, maxLength: 255, default: openclaw }
        action: { type: string, minLength: 1, maxLength: 255 }
        context: { type: object, additionalProperties: true, default: {} }
        reason: { type: string, minLength: 1, maxLength: 1000 }
        category: { type: string, enum: [enduser, engineer, finance, security, compliance, other], default: engineer }
        rule_code: { type: string, minLength: 1, maxLength: 64 }
        timeout_seconds: { type: integer, minimum: 10, maximum: 120, default: 120 }
    HitlRespondRequest:
      description: >
        The request shape depends on the target item's `category`. `enduser`
        items use the approve/reject form; all other categories use the
        dismiss/create-rule form.
      oneOf:
        - $ref: '#/components/schemas/HitlRespondEnduser'
        - $ref: '#/components/schemas/HitlRespondEngineer'
    HitlRespondEnduser:
      type: object
      required: [decision, responded_by]
      properties:
        decision: { type: string, enum: [approved, rejected] }
        responded_by: { type: string, minLength: 1, maxLength: 255 }
    HitlRespondEngineer:
      type: object
      required: [action, responded_by]
      properties:
        action: { type: string, enum: [dismiss, create_rule] }
        responded_by: { type: string, minLength: 1, maxLength: 255 }
        linked_rule_id:
          type: string
          format: uuid
          description: Required when `action` is `create_rule`.
    HitlRespondResponse:
      type: object
      properties:
        id: { type: string, format: uuid }
        decision_id: { type: [string, "null"], format: uuid }
        status: { type: string, enum: [approved, rejected, dismissed, resolved] }
        responded_at: { type: string, format: date-time }
        responded_by: { type: string }

    # ── Settings ──
    NoCoverageDefaults:
      type: object
      properties:
        read: { type: string, enum: [approve, deny, ask] }
        write: { type: string, enum: [approve, deny, ask] }
        delete: { type: string, enum: [approve, deny, ask] }
        admin: { type: string, enum: [approve, deny, ask] }
    NotificationChannel:
      type: object
      required: [type, config]
      properties:
        type: { type: string, minLength: 1, maxLength: 50 }
        config: { type: object, additionalProperties: true }
    Settings:
      type: object
      properties:
        id: { type: string, format: uuid }
        no_coverage_defaults: { $ref: '#/components/schemas/NoCoverageDefaults' }
        default_agent_mode: { type: string, enum: [enforce, monitor, off] }
        autopilot_enabled: { type: boolean }
        hitl_timeout_seconds: { type: integer }
        notification_channels:
          type: array
          items: { $ref: '#/components/schemas/NotificationChannel' }
        enduser_hitl_enabled: { type: boolean }
        recall_retain_masked_original: { type: boolean }
        auto_disable_high_interference: { type: boolean }
        recall_floor_disabled_detectors:
          type: array
          items: { type: string }
        recall_floor_enabled_optin:
          type: array
          items: { type: string }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    SettingsUpdate:
      type: object
      minProperties: 1
      description: Partial update — only supplied keys change.
      properties:
        no_coverage_defaults:
          type: object
          description: Any subset of read/write/delete/admin.
          properties:
            read: { type: string, enum: [approve, deny, ask] }
            write: { type: string, enum: [approve, deny, ask] }
            delete: { type: string, enum: [approve, deny, ask] }
            admin: { type: string, enum: [approve, deny, ask] }
        default_agent_mode: { type: string, enum: [enforce, monitor, off] }
        autopilot_enabled: { type: boolean }
        hitl_timeout_seconds: { type: integer, minimum: 30, maximum: 120 }
        notification_channels:
          type: array
          items: { $ref: '#/components/schemas/NotificationChannel' }
        enduser_hitl_enabled: { type: boolean }
        recall_retain_masked_original: { type: boolean }
        auto_disable_high_interference: { type: boolean }
        recall_floor_disabled_detectors:
          type: array
          items: { type: string }
        recall_floor_enabled_optin:
          type: array
          items: { type: string }

    # ── Delegation ──
    GrantCreate:
      type: object
      required: [parent_agent_id, child_agent_id]
      properties:
        parent_agent_id: { type: string, minLength: 1, maxLength: 255 }
        child_agent_id: { type: string, minLength: 1, maxLength: 255 }
        tool_scope:
          type: [array, "null"]
          default: null
          items: { type: string, minLength: 1 }
          description: Allowlist of tools the child may use. `null` = unrestricted; `[]` = deny-all.
        context_scope:
          type: [array, "null"]
          default: null
          items: { type: string, minLength: 1 }
        purpose: { type: string, maxLength: 2000 }
        duration_seconds: { type: integer, minimum: 1, maximum: 604800, default: 3600 }
        max_depth: { type: integer, minimum: 1, maximum: 16 }
        parent_grant_token: { type: [string, "null"] }
    GrantCreateResponse:
      type: object
      properties:
        grant_id: { type: string }
        status: { type: string, enum: [pending] }
        effective_tool_scope: { type: [array, "null"], items: { type: string } }
        effective_context_scope: { type: [array, "null"], items: { type: string } }
        accept_deadline: { type: string, format: date-time }
    Grant:
      type: object
      properties:
        id: { type: string }
        status: { type: string, enum: [pending, active, revoked] }
        parent_agent_id: { type: string }
        child_agent_id: { type: string }
        depth: { type: integer }
        max_depth: { type: integer }
        effective_tool_scope: { type: [array, "null"], items: { type: string } }
        effective_context_scope: { type: [array, "null"], items: { type: string } }
        purpose: { type: [string, "null"] }
        accept_deadline: { type: string, format: date-time }
        created_at: { type: string, format: date-time }
        accepted_at: { type: [string, "null"], format: date-time }
        expires_at: { type: [string, "null"], format: date-time }
        revoked_at: { type: [string, "null"], format: date-time }
        revocation_reason: { type: [string, "null"] }
    GrantAcceptResponse:
      type: object
      properties:
        grant_token: { type: string, description: The signed token to present on delegated evaluations. }
        tool_scope: { type: [array, "null"], items: { type: string } }
        context_scope: { type: [array, "null"], items: { type: string } }
        expires_at: { type: string, format: date-time }
    GrantRevokeRequest:
      type: object
      properties:
        reason: { type: string, maxLength: 2000 }
    GrantRevokeResponse:
      type: object
      properties:
        revoked_count: { type: integer }

    # ── Records ──
    Signature:
      type: object
      required: [algorithm, value]
      properties:
        algorithm: { type: string, minLength: 1, maxLength: 100 }
        value: { type: string, minLength: 1 }
        keyId: { type: string, maxLength: 255 }
        signedAt: { type: string, format: date-time }
    RecordEnvelope:
      type: object
      required: [source, tenantId, correlationId, event]
      properties:
        source:
          type: string
          enum: [execute, browser_proxy, session_events, action, isolate, app, retrieval]
        tenantId: { type: string, minLength: 1, maxLength: 255, description: Must match the authenticated account. }
        correlationId: { type: string, minLength: 1, maxLength: 255 }
        authorization:
          type: object
          properties:
            type: { type: string, maxLength: 100 }
            principal: { type: string, maxLength: 255 }
            scope: { type: string, maxLength: 500 }
        actor:
          $ref: '#/components/schemas/RecordParty'
        subject:
          $ref: '#/components/schemas/RecordParty'
        event:
          type: object
          required: [type, occurredAt, payload]
          properties:
            type: { type: string, minLength: 1, maxLength: 255 }
            occurredAt: { type: string, format: date-time }
            payload: { type: object, additionalProperties: true }
            signature: { $ref: '#/components/schemas/Signature' }
        artifacts:
          type: array
          items:
            type: object
            required: [artifactType, contentHash]
            properties:
              artifactType: { type: string, minLength: 1, maxLength: 100 }
              mimeType: { type: string, maxLength: 255 }
              storageRef: { type: string, maxLength: 1000 }
              contentHash: { type: string, minLength: 1, maxLength: 255 }
              metadata: { type: object, additionalProperties: true }
        attestations:
          type: array
          items:
            type: object
            required: [attestationType, issuerType, issuerId, statement, hash]
            properties:
              attestationType: { type: string, minLength: 1, maxLength: 100 }
              issuerType: { type: string, minLength: 1, maxLength: 100 }
              issuerId: { type: string, minLength: 1, maxLength: 255 }
              statement: { type: object, additionalProperties: true }
              hash: { type: string, minLength: 1, maxLength: 255 }
              signature: { $ref: '#/components/schemas/Signature' }
    RecordParty:
      type: object
      description: An actor or subject reference.
      properties:
        type: { type: string, maxLength: 100 }
        id: { type: string, maxLength: 255 }
        name: { type: string, maxLength: 255 }
        metadata: { type: object, additionalProperties: true }
    EnvelopeIngestResponse:
      type: object
      required: [record_id, event_id]
      properties:
        record_id: { type: string, format: uuid }
        event_id: { type: string, format: uuid }
    VerifyResult:
      type: object
      description: >
        The per-check verification result (integrity, leaf signature, Merkle
        inclusion, signed root, and RFC-3161 timestamp). The concrete field set
        is produced by the verification service; the common shape is a set of
        boolean checks plus supporting detail.
      additionalProperties: true
    Record:
      type: object
      properties:
        id: { type: string, format: uuid }
        kind: { type: string }
        status: { type: string, enum: [open, finalized, superseded] }
        correlation_id: { type: string }
        root_session_id: { type: [string, "null"] }
        integrity_state: { type: string }
        created_at: { type: string, format: date-time }
        finalized_at: { type: [string, "null"], format: date-time }
    RecordListPage:
      allOf:
        - $ref: '#/components/schemas/PageEnvelopeLimit'
        - type: object
          properties:
            data:
              type: array
              items: { $ref: '#/components/schemas/Record' }
    RecordEvent:
      type: object
      properties:
        id: { type: string, format: uuid }
        event_type: { type: string }
        event_source: { type: string }
        sequence_no: { type: integer }
        occurred_at: { type: string, format: date-time }
        payload_json: { type: object, additionalProperties: true }
        hash: { type: string }
        signature: { type: [string, "null"] }
    RecordEventPage:
      allOf:
        - $ref: '#/components/schemas/PageEnvelopeLimit'
        - type: object
          properties:
            data:
              type: array
              items: { $ref: '#/components/schemas/RecordEvent' }
    RecordArtifact:
      type: object
      properties:
        id: { type: string, format: uuid }
        artifact_type: { type: string }
        mime_type: { type: [string, "null"] }
        storage_ref: { type: [string, "null"] }
        content_hash: { type: string }
        metadata_json: { type: [object, "null"], additionalProperties: true }
        created_at: { type: string, format: date-time }
    RecordArtifactPage:
      allOf:
        - $ref: '#/components/schemas/PageEnvelopeLimit'
        - type: object
          properties:
            data:
              type: array
              items: { $ref: '#/components/schemas/RecordArtifact' }
    RecordAttestation:
      type: object
      properties:
        id: { type: string, format: uuid }
        attestation_type: { type: string }
        issuer_type: { type: string }
        issuer_id: { type: string }
        statement_json: { type: object, additionalProperties: true }
        hash: { type: string }
        signature: { type: [string, "null"] }
        created_at: { type: string, format: date-time }
    RecordAttestationPage:
      allOf:
        - $ref: '#/components/schemas/PageEnvelopeLimit'
        - type: object
          properties:
            data:
              type: array
              items: { $ref: '#/components/schemas/RecordAttestation' }
    Checkpoint:
      type: object
      description: >
        A tenant-neutral transparency-log checkpoint (signed Merkle root, entry
        counts, timestamp, and witness status). Field set is produced by the
        checkpoint-feed service.
      additionalProperties: true
    CheckpointPage:
      type: object
      properties:
        data:
          type: array
          items: { $ref: '#/components/schemas/Checkpoint' }
        hasMore: { type: boolean }
    ConsistencyResult:
      type: object
      description: >
        A hash-chain consistency proof over a checkpoint range. `verified: false`
        is returned in a 200 body when the chain cannot be proven consistent.
      properties:
        from: { type: integer }
        to: { type: integer }
        count: { type: integer }
        verified: { type: boolean }
        contiguous: { type: boolean }
      additionalProperties: true

    # ── Audit log entries ──
    ActionAuditLogEntry:
      type: object
      properties:
        id: { type: string, format: uuid }
        agent_id: { type: string }
        target_app: { type: string }
        action: { type: string }
        context: { type: object, additionalProperties: true }
        decision: { type: string, enum: [permit, deny, approval_required, mask] }
        reason: { type: [string, "null"] }
        rule_id: { type: [string, "null"] }
        hitl_result: { type: [string, "null"], enum: [approved, rejected, timeout, null] }
        hitl_responded_at: { type: [string, "null"], format: date-time }
        hitl_responded_by: { type: [string, "null"] }
        created_at: { type: string, format: date-time }
        execution_result: { type: [string, "null"] }
        execution_completed_at: { type: [string, "null"], format: date-time }
        execution_details: { type: [object, "null"], additionalProperties: true }
    ActionAuditLogPage:
      allOf:
        - $ref: '#/components/schemas/PageEnvelope'
        - type: object
          properties:
            data:
              type: array
              items: { $ref: '#/components/schemas/ActionAuditLogEntry' }
    DecisionDetail:
      allOf:
        - $ref: '#/components/schemas/ActionAuditLogEntry'
        - type: object
          properties:
            vendor_id: { type: string }
            hitl:
              description: The linked approval item when the decision was `approval_required`, else null.
              oneOf:
                - type: object
                  properties:
                    id: { type: string, format: uuid }
                    status: { type: string }
                    category: { type: string }
                    expires_at: { type: [string, "null"], format: date-time }
                    responded_at: { type: [string, "null"], format: date-time }
                    responded_by: { type: [string, "null"] }
                    created_at: { type: string, format: date-time }
                - type: "null"
    RetrievalAuditLogEntry:
      type: object
      properties:
        id: { type: string, format: uuid }
        agent_id: { type: string }
        operation: { type: string }
        resource_type: { type: string }
        decision: { type: string, enum: [allow, deny, redact, escalate] }
        reason_code: { type: string }
        reason: { type: [string, "null"] }
        rule_id: { type: [string, "null"] }
        receipt_id: { type: [string, "null"] }
        metadata: { type: [object, "null"], additionalProperties: true }
        created_at: { type: string, format: date-time }
    RetrievalAuditLogPage:
      allOf:
        - $ref: '#/components/schemas/PageEnvelope'
        - type: object
          properties:
            data:
              type: array
              items: { $ref: '#/components/schemas/RetrievalAuditLogEntry' }
    RecordAuditLogEntry:
      type: object
      properties:
        id: { type: string, format: uuid }
        record_id: { type: string, format: uuid }
        event_type: { type: string }
        event_source: { type: string }
        sequence_no: { type: integer }
        occurred_at: { type: string, format: date-time }
        payload_json: { type: object, additionalProperties: true }
        hash: { type: string }
        signature: { type: [string, "null"] }
    RecordAuditLogPage:
      allOf:
        - $ref: '#/components/schemas/PageEnvelopeLimit'
        - type: object
          properties:
            data:
              type: array
              items: { $ref: '#/components/schemas/RecordAuditLogEntry' }

    # ── Agent Cortex cognition ──
    CognitionSessionEntry:
      type: object
      description: >
        One captured cognition session (natural key `session_id`; there is no
        surrogate id). `vendor_id` is never returned.
      properties:
        session_id: { type: string }
        root_agent_id: { type: string }
        agent_display_name: { type: [string, "null"] }
        framework: { type: [string, "null"] }
        capture_enabled: { type: [boolean, "null"] }
        sensitive_storage_enabled: { type: [boolean, "null"] }
        agents:
          type: [array, "null"]
          description: Folded sub-agent roster (spawned children union).
          items: { type: object, additionalProperties: true }
        workflows:
          type: [array, "null"]
          items: { type: object, additionalProperties: true }
        sensorium: { type: [object, "null"], additionalProperties: true }
        started_at: { type: [string, "null"], format: date-time }
        last_event_at: { type: [string, "null"], format: date-time }
        event_count: { type: [integer, "null"] }
    CognitionSessionPage:
      allOf:
        - $ref: '#/components/schemas/PageEnvelope'
        - type: object
          properties:
            data:
              type: array
              items: { $ref: '#/components/schemas/CognitionSessionEntry' }
    CognitionEventEntry:
      type: object
      description: >
        One cognition beat. Stored `content`/`tool`/`result`/`spawn_prompt` are
        floor-redacted copies; the encrypted raw and its hash are never served
        by this endpoint (reveal is the audited unmask flow). `cost` carries
        raw token buckets only (no dollars) — price at read time against the
        beat's `model`.
      properties:
        id: { type: string, format: uuid }
        agent_id: { type: string }
        session_id: { type: string }
        thread_id: { type: [string, "null"] }
        parent_event_id: { type: [string, "null"] }
        seq: { type: integer }
        client_event_id: { type: [string, "null"] }
        ts: { type: string, format: date-time }
        duration_ms: { type: [integer, "null"] }
        kind:
          type: string
          enum: [thought, speech, action, observation, intervention, spawn, phase, handoff, lifecycle, error, memory]
        visibility: { type: [string, "null"], enum: [shown, hidden, null] }
        status: { type: [string, "null"] }
        provenance: { type: [string, "null"], enum: [signed, parsed, proxy, narration, null] }
        revised: { type: [boolean, "null"] }
        content: { type: [object, "null"], additionalProperties: true }
        tool: { type: [object, "null"], additionalProperties: true }
        result: { type: [object, "null"], additionalProperties: true }
        governance: { type: [object, "null"], additionalProperties: true }
        decision_id: { type: [string, "null"], format: uuid }
        spawn_child_agent_id: { type: [string, "null"] }
        spawn_child_name: { type: [string, "null"] }
        spawn_kind: { type: [string, "null"], enum: [subagent, workflow, teammate, null] }
        spawn_model: { type: [string, "null"] }
        spawn_prompt: { type: [string, "null"] }
        phase: { type: [object, "null"], additionalProperties: true }
        handoff: { type: [object, "null"], additionalProperties: true }
        model: { type: [string, "null"] }
        agent_role: { type: [string, "null"] }
        framework: { type: [string, "null"] }
        attribution: { type: [object, "null"], additionalProperties: true }
        memory:
          type: [object, "null"]
          description: >-
            Structural memory-access facet, present on `kind: memory` beats.
            Typed scalars only — the human-readable memory key / search query
            rides in the redacted `tool.argSummary` or `content.text`.
          properties:
            op: { type: string, enum: [read, write, search, delete] }
            store: { type: string }
            scope: { type: string, enum: [session, agent, user, org] }
            itemCount: { type: integer }
            bytes: { type: integer }
            hit: { type: boolean }
        cost:
          type: [object, "null"]
          description: Non-negative integer token buckets (no stored dollars).
          properties:
            tokensIn: { type: integer }
            tokensOut: { type: integer }
            cacheRead: { type: integer }
            cacheWrite: { type: integer }
        created_at: { type: string, format: date-time }
    CognitionEventPage:
      allOf:
        - $ref: '#/components/schemas/PageEnvelope'
        - type: object
          properties:
            data:
              type: array
              items: { $ref: '#/components/schemas/CognitionEventEntry' }

    # ── Pagination envelopes ──
    PageEnvelope:
      type: object
      description: Offset pagination envelope (`data` + `total`/`page`/`pageSize`).
      properties:
        total: { type: integer }
        page: { type: integer }
        pageSize: { type: integer }
    PageEnvelopeLimit:
      type: object
      description: Offset pagination envelope that reports `limit` instead of `pageSize`.
      properties:
        total: { type: integer }
        page: { type: integer }
        limit: { type: integer }
