openapi: 3.0.3
info:
  title: AxonFlow Orchestrator API
  description: >
    REST API for the AxonFlow Orchestrator service - Dynamic Policy Enforcement,
    LLM Routing, and Multi-Agent Planning.


    The Orchestrator handles:

    - **Dynamic Policy Evaluation**: Database-backed policy rules with risk
    scoring

    - **LLM Routing**: Intelligent routing across OpenAI, Anthropic, Bedrock,
    Ollama

    - **Multi-Agent Planning (MAP)**: LLM-powered task decomposition and
    parallel execution

    - **Response Processing**: PII detection and redaction

    - **Workflow Execution**: Step-based workflow orchestration

    - **Audit Logging**: Comprehensive request/response logging


    ## Architecture


    ```

    Client → Agent → Orchestrator → LLM Providers
                  ↘ MCP Connectors
    ```


    The Orchestrator receives pre-authenticated requests from the Agent and
    handles

    complex processing including LLM calls and dynamic policy evaluation.


    ## Internal API


    These endpoints are typically called by the Agent service, not directly by
    clients.

    For client-facing APIs, see the Agent API specification.
  version: 1.1.0
  contact:
    name: AxonFlow Support
    url: https://getaxonflow.com/support
  license:
    name: Business Source License 1.1
    url: https://github.com/getaxonflow/axonflow/blob/main/LICENSE
servers:
  - url: https://orchestrator.getaxonflow.com
    description: Production (SaaS)
  - url: http://localhost:8081
    description: Local Development
tags:
  - name: Health
    description: Service health and readiness
  - name: Processing
    description: Main request processing pipeline
  - name: Multi-Agent Planning
    description: LLM-powered task decomposition and execution
  - name: Agents
    description: |
      Agent configuration management (MAP 0.8). **Enterprise only** — the
      `/api/v1/agents` route family is registered only in Enterprise builds
      with a database connection; Community returns 404.
  - name: LLM Providers
    description: LLM provider management
  - name: Dynamic Policies
    description: Runtime policy management
  - name: Workflows
    description: Workflow execution engine
  - name: Workflow Control Plane
    description: >
      Governance gates for external orchestrators (LangChain, LangGraph,
      CrewAI).

      "LangChain runs the workflow. AxonFlow decides when it's allowed to move
      forward."


      Features:

      - Register workflows from external orchestrators

      - Check step gates before each workflow step

      - Apply policies at step transitions (allow/block/require_approval)

      - Track workflow lifecycle (in_progress/completed/aborted/failed)
  - name: Connectors
    description: Connector marketplace
  - name: Audit
    description: Audit log search and retrieval
  - name: SEBI Compliance
    description: |
      SEBI AI/ML Guidelines compliance and DPDP Act 2023 audit exports.
      Enterprise feature for Indian financial services compliance.
  - name: RBI Compliance
    description: |
      RBI FREE-AI Framework compliance for Indian banking institutions.
      Enterprise feature providing AI System Registry, Model Validation,
      Incident Management, Kill Switch, Board Reporting, and Audit Export.
  - name: EU AI Act
    description: >
      EU AI Act compliance endpoints for technical documentation export,

      conformity assessments (Article 43), and accuracy/bias tracking (Article
      15).

      Enterprise feature for EU regulatory compliance.
  - name: OJK Compliance
    description: >
      OJK AI Governance + UU PDP compliance for Indonesian financial services.

      Audit export, retention/readiness checks, UU PDP Art. 46
      breach-notification

      lifecycle, and a compliance dashboard. **Enterprise** feature.
  - name: Decisions & Overrides
    description: |
      Decision explainability (ADR-043) and session-scoped policy overrides
      (ADR-044). List recent governance decisions, explain a specific decision,
      and create/list/revoke time-boxed policy overrides.
  - name: Metrics
    description: Performance monitoring
  - name: Decision & Execution Replay
    description: >
      Decision & Execution Replay API for debugging, auditing, and compliance.

      Captures every step of workflow execution with full input/output snapshots
      and policy decisions.
  - name: Webhooks
    description: >
      Webhook subscription management for real-time event notifications.

      Subscribe to events like policy violations, workflow completions, budget
      alerts, etc.
  - name: Cost Controls
    description: |
      Budget management and LLM usage tracking for cost optimization.
      Supports budgets at organization, team, agent, workflow, and user scopes.
      Provides usage summaries, breakdowns, and pre-request budget checks.
  - name: Unified Executions
    description: >
      Unified execution tracking and real-time streaming for MAP plans and WCP
      workflows.

      SSE streaming provides real-time status updates. Community: 5 concurrent
      connections

      per tenant. Enterprise: Unlimited.
  - name: Media Governance
    description: >
      Multimodal image governance for LLM requests.

      Analyzes images for PII (via OCR), content safety, face/biometric
      detection, and document classification.

      Community tier provides fail-open governance with audit trail.

      Enterprise tier adds configurable enforcement and cloud analyzers.
paths:
  /health:
    get:
      tags:
        - Health
      summary: Health check
      description: |
        Returns service health status including component health:
        - Policy Engine
        - LLM Router
        - Response Processor
        - Audit Logger
        - Workflow Engine
        - Planning Engine (MAP)
        - Result Aggregator (MAP)
      operationId: healthCheck
      responses:
        '200':
          description: Service health status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
              example:
                status: healthy
                service: axonflow-orchestrator
                version: 9.8.0
                timestamp: '2025-01-15T10:30:00Z'
                components:
                  policy_engine: true
                  llm_router: true
                  response_processor: true
                  audit_logger: true
                  workflow_engine: true
                  planning_engine: true
                  result_aggregator: true
                features:
                  multi_agent_planning: true
  /metrics:
    get:
      tags:
        - Metrics
      summary: Get performance metrics (JSON)
      description: |
        Returns comprehensive JSON metrics including:
        - Request counts and rates
        - Latency percentiles (P50, P95, P99)
        - Per-stage timing (dynamic policy, LLM)
        - Per-provider metrics (tokens, cost)
        - Health status
      operationId: getMetrics
      responses:
        '200':
          description: Performance metrics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MetricsResponse'
              example:
                orchestrator_metrics:
                  uptime_seconds: 3600
                  total_requests: 5000
                  success_requests: 4800
                  failed_requests: 100
                  blocked_requests: 100
                  success_rate: 96
                  rps: 1.38
                  error_rate_per_sec: 0.027
                  dynamic_policy_eval_p50_ms: 2.5
                  dynamic_policy_eval_p95_ms: 8
                  dynamic_policy_eval_p99_ms: 15
                  llm_routing_p50_ms: 500
                  llm_routing_p95_ms: 1500
                  llm_routing_p99_ms: 3000
                health:
                  up: 1
                  consecutive_errors: 0
                providers:
                  openai:
                    total_calls: 3000
                    success_calls: 2950
                    failed_calls: 50
                    total_tokens: 1500000
                    total_cost: 45.5
                    p99_ms: 2500
                  bedrock:
                    total_calls: 2000
                    success_calls: 1990
                    failed_calls: 10
                    total_tokens: 800000
                    total_cost: 12
                    p99_ms: 1800
                timestamp: '2025-01-15T10:30:00Z'
  /prometheus:
    get:
      tags:
        - Metrics
      summary: Prometheus metrics endpoint
      description: Returns metrics in Prometheus exposition format
      operationId: getPrometheusMetrics
      responses:
        '200':
          description: Prometheus metrics
          content:
            text/plain:
              schema:
                type: string
  /api/v1/process:
    post:
      tags:
        - Processing
      summary: Process orchestrator request
      description: >
        Main processing endpoint. Handles:

        1. Dynamic policy evaluation

        2. LLM provider routing

        3. Response processing (PII detection)

        4. Audit logging

        5. Metrics collection


        **Note**: This endpoint is typically called by the Agent, not directly
        by clients.

        For MCP queries (`request_type: mcp-query`), routes to the Agent MCP
        handler.
      operationId: processRequest
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrchestratorRequest'
            examples:
              llmChat:
                summary: LLM chat request
                value:
                  request_id: req_12345
                  query: Summarize the quarterly report
                  request_type: llm_chat
                  user:
                    id: 123
                    email: analyst@company.com
                    role: analyst
                    permissions:
                      - query
                      - llm_chat
                    tenant_id: tenant-abc
                  client:
                    id: analytics-app
                    name: Analytics Dashboard
                    org_id: org-123
                    tenant_id: tenant-abc
                  context:
                    provider: openai
                    strict_provider: false
                    model_preference: gpt-4
                  timestamp: '2025-01-15T10:30:00Z'
              skipLLM:
                summary: Skip LLM (testing)
                value:
                  request_id: test_001
                  query: Test query
                  request_type: llm_chat
                  skip_llm: true
                  user:
                    id: 1
                    email: test@test.com
                    role: tester
                    tenant_id: test-tenant
                  client:
                    id: test-client
                    name: Test
                    tenant_id: test-tenant
                  timestamp: '2025-01-15T10:30:00Z'
      responses:
        '200':
          description: Request processed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrchestratorResponse'
              example:
                request_id: req_12345
                success: true
                data: Here is the quarterly report summary...
                redacted: false
                policy_info:
                  allowed: true
                  applied_policies:
                    - rate-limit
                    - content-filter
                  risk_score: 0.15
                  processing_time_ms: 5
                provider_info:
                  provider: openai
                  model: gpt-4
                  response_time_ms: 1250
                  tokens_used: 350
                  cost: 0.021
                processing_time: 1.3s
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          description: Blocked by dynamic policy
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrchestratorResponse'
              example:
                request_id: req_12345
                success: false
                error: Request blocked by dynamic policy
                policy_info:
                  allowed: false
                  applied_policies:
                    - high-risk-content
                  risk_score: 0.85
                  required_actions:
                    - approval_required
                processing_time: 8ms
        '500':
          $ref: '#/components/responses/InternalError'
  /api/v1/plan:
    post:
      tags:
        - Multi-Agent Planning
      summary: Execute multi-agent plan
      description: >
        Multi-Agent Planning (MAP) endpoint for complex, multi-step tasks.


        ## How MAP Works

        1. **Decomposition**: LLM analyzes query and breaks into sub-tasks

        2. **Planning**: Creates workflow with dependencies

        3. **Execution**: Runs tasks (parallel when possible)

        4. **Aggregation**: Synthesizes results into final response


        ## Execution Modes

        - `auto`: Automatically determines parallel/sequential (recommended)

        - `parallel`: Force parallel execution of all independent steps

        - `sequential`: Force sequential step-by-step execution

        - `balanced`: I/O-bound connector steps parallel, LLM steps sequential

        - `confirm`: Every step requires explicit approval (Enterprise only)

        - `step`: First step auto-executes, subsequent require approval
        (Enterprise only)


        ## Domains

        - `travel`: Flights, hotels, itineraries

        - `healthcare`: Medical queries

        - `finance`: Financial analysis

        - `generic`: General-purpose tasks


        Plan steps are automatically routed to matching connectors based on
        capabilities.

        Community: Subject to connector limits (2 connectors).

        Enterprise: Unlimited connectors with multi-connector fallback support.


        **Requires authentication**: Requests must come through the Agent.
      operationId: executePlan
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PlanRequest'
            examples:
              travelPlan:
                summary: Travel planning
                value:
                  query: Find flights from NYC to LAX next week and suggest hotels
                  domain: travel
                  execution_mode: auto
                  user:
                    id: 123
                    email: traveler@company.com
                    role: employee
                    permissions:
                      - query
                      - mcp_query
                    tenant_id: tenant-abc
                  client:
                    id: travel-planner
                    name: Travel Planning App
                  context:
                    departure_date: '2025-01-20'
                    return_date: '2025-01-25'
                    budget: 1500
              genericPlan:
                summary: Generic task
                value:
                  query: Research competitor pricing and create a summary report
                  domain: generic
                  execution_mode: sequential
                  user:
                    id: 456
                    email: analyst@company.com
                    role: analyst
                    permissions:
                      - query
                      - llm_chat
                    tenant_id: tenant-xyz
      responses:
        '200':
          description: Plan executed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlanResponse'
              example:
                success: true
                plan_id: plan_1705312200_abc123
                workflow_execution_id: exec_xyz789
                result:
                  flights:
                    - flight_number: UA123
                      price: 299
                      departure: '2025-01-20T08:00:00Z'
                  hotels:
                    - name: Hilton LAX
                      price_per_night: 189
                      rating: 4.5
                  summary: Found 5 flights and 3 hotels within budget
                metadata:
                  tasks_executed: 3
                  execution_mode: parallel
                  execution_time_ms: 3500
                  tasks:
                    - name: search_flights
                      status: completed
                      time_ms: 1200
                    - name: search_hotels
                      status: completed
                      time_ms: 1100
                    - name: synthesize_results
                      status: completed
                      time_ms: 800
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error: >-
                  Authentication required: requests must be routed through
                  AxonFlow Agent
        '503':
          description: Planning engine unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/plan/execute:
    post:
      tags:
        - Multi-Agent Planning
      summary: Execute a stored plan
      description: >
        Executes a plan previously generated and stored via `POST /api/v1/plan`.

        The plan to execute is identified by `context.plan_id` in the request
        body.

        The execution mode is taken from the **stored plan** (not this request):

        `auto`, `parallel`, `sequential`, `balanced`, or the Enterprise-only

        HITL modes `confirm` / `step`.


        **Requires authentication**: requests must be routed through the
        AxonFlow

        Agent (`user.id` must be set). The `X-Tenant-ID` / `X-Org-ID` headers
        set

        by the Agent auth chain override any identity fields in the body.


        A plan can be executed once: re-executing returns 409, a cancelled plan

        returns 409, and an expired plan returns 410.
      operationId: executeStoredPlan
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PlanRequest'
            example:
              query: Find flights from NYC to LAX next week
              domain: travel
              user:
                id: 123
                email: traveler@company.com
                role: employee
                permissions:
                  - query
                  - mcp_query
                tenant_id: tenant-abc
              context:
                plan_id: plan_1705312200_abc123
      responses:
        '200':
          description: |
            Plan executed successfully. Body is a PlanResponse for normal
            execution. HITL `confirm`/`step` dispatch returns an inline object
            (`plan_id`, `workflow_id`, `status`, `current_step`, `total_steps`,
            `step_name`, `approval_info`) instead.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlanResponse'
        '202':
          description: Execution paused awaiting HITL approval
          content:
            application/json:
              schema:
                type: object
                properties:
                  plan_id:
                    type: string
                  execution_id:
                    type: string
                  status:
                    type: string
                    example: paused
                  paused_at_step:
                    type: string
                  paused_reason:
                    type: string
                  approval_id:
                    type: string
        '400':
          description: Invalid request body or missing context.plan_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Not routed through AxonFlow Agent (user.id missing)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: >-
            Policy blocked execution, or HITL confirm/step mode requested in
            Community edition
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlanResponse'
        '404':
          description: Plan not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Plan already executed or cancelled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '410':
          description: Plan expired
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Concurrent plan-execution limit reached
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: object
                    properties:
                      code:
                        type: string
                        example: CONCURRENT_EXECUTION_LIMIT
                      message:
                        type: string
        '503':
          description: Workflow engine or plan storage unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/plan/{id}:
    get:
      tags:
        - Multi-Agent Planning
      summary: Get plan execution status
      description: |
        Retrieve the status of a plan by ID.

        Returns detailed execution status including:
        - Overall plan status (pending, executing, completed, failed, expired)
        - Step-level progress with completion percentage
        - Duration and cost tracking
        - Error details if execution failed

        **New in #1075:** Response now includes unified execution tracking with:
        - `steps` array with individual step status
        - `progress_percent` for real-time progress
        - `duration` for elapsed time
        - `estimated_cost_usd` and `actual_cost_usd` for cost tracking
      operationId: getPlanStatus
      parameters:
        - name: id
          in: path
          required: true
          description: Plan ID (e.g., plan_1705312200_abc123)
          schema:
            type: string
      responses:
        '200':
          description: Plan status retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlanStatusResponse'
              examples:
                pending:
                  summary: Pending plan
                  value:
                    plan_id: plan_1705312200_abc123
                    execution_id: plan_xyz789
                    status: pending
                    query: Find flights from NYC to LAX
                    domain: travel
                    total_steps: 3
                    completed_steps: 0
                    progress_percent: 0
                    created_at: '2025-01-15T10:00:00Z'
                    expires_at: '2025-01-15T12:00:00Z'
                executing:
                  summary: Executing plan with step progress
                  value:
                    plan_id: plan_1705312200_abc123
                    execution_id: plan_xyz789
                    status: executing
                    query: Research competitor pricing
                    domain: generic
                    total_steps: 3
                    completed_steps: 1
                    progress_percent: 33.33
                    duration: 15s
                    started_at: '2025-01-15T10:00:00Z'
                    steps:
                      - step_id: step_0_analyze
                        step_index: 0
                        step_name: analyze
                        step_type: llm_call
                        status: completed
                        duration: 8s
                        model: gpt-4
                        provider: openai
                      - step_id: step_1_research
                        step_index: 1
                        step_name: research
                        step_type: llm_call
                        status: running
                        started_at: '2025-01-15T10:00:08Z'
                      - step_id: step_2_synthesize
                        step_index: 2
                        step_name: synthesize
                        step_type: llm_call
                        status: pending
                completed:
                  summary: Completed plan with costs
                  value:
                    plan_id: plan_1705312200_abc123
                    execution_id: plan_xyz789
                    status: completed
                    query: Analyze sales data
                    domain: finance
                    total_steps: 2
                    completed_steps: 2
                    progress_percent: 100
                    duration: 25s
                    estimated_cost_usd: 0.05
                    actual_cost_usd: 0.042
                    created_at: '2025-01-15T10:00:00Z'
                    started_at: '2025-01-15T10:00:00Z'
                    completed_at: '2025-01-15T10:00:25Z'
                    steps:
                      - step_id: step_0_query
                        step_index: 0
                        step_name: query_data
                        step_type: connector_call
                        status: completed
                        duration: 5s
                        cost_usd: 0.002
                      - step_id: step_1_analyze
                        step_index: 1
                        step_name: analyze
                        step_type: llm_call
                        status: completed
                        duration: 20s
                        cost_usd: 0.04
                        model: claude-sonnet-4
                        provider: anthropic
        '404':
          description: Plan not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error: 'Plan not found: plan_nonexistent'
        '410':
          description: Plan expired
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error: 'Plan has expired: plan_1705312200_abc123'
        '503':
          description: Plan service unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    put:
      tags:
        - Multi-Agent Planning
      summary: Update a pending plan
      description: >
        Update a plan that has not yet been executed.

        Uses optimistic locking via the `version` field — the request must
        include

        the expected current version. If the version doesn't match, returns 409.


        Only plans with status `pending` can be updated.
      operationId: updatePlan
      parameters:
        - name: id
          in: path
          required: true
          description: Plan ID to update
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdatePlanRequest'
            example:
              version: 1
              execution_mode: parallel
      responses:
        '200':
          description: Plan updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UpdatePlanResponse'
              example:
                success: true
                plan_id: plan_1705312200_abc123
                version: 2
                status: pending
        '404':
          description: Plan not found
        '409':
          description: Version conflict (plan was modified by another request)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error: 'Version conflict: expected version 1, current version is 2'
  /api/v1/plan/{id}/cancel:
    post:
      tags:
        - Multi-Agent Planning
      summary: Cancel a pending plan
      description: |
        Cancel a plan that has not yet completed execution.
        Only plans with status `pending` or `executing` can be cancelled.
        Returns 409 if the plan is already completed or cancelled.
      operationId: cancelPlan
      parameters:
        - name: id
          in: path
          required: true
          description: Plan ID to cancel
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                reason:
                  type: string
                  description: Optional cancellation reason
                  example: User requested cancellation
      responses:
        '200':
          description: Plan cancelled successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CancelPlanResponse'
              example:
                success: true
                plan_id: plan_1705312200_abc123
                status: cancelled
        '404':
          description: Plan not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Plan cannot be cancelled (already completed or cancelled)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error: Plan is already completed and cannot be cancelled
  /api/v1/plan/{id}/versions:
    get:
      tags:
        - Multi-Agent Planning
      summary: Get plan version history
      description: |
        Retrieve the version history for a plan, showing all changes made.
        Each version entry includes the change type, who made it, and when.

        Community: Max 10 versions per plan, max 25 plans with versioning.
        Enterprise: Unlimited.
      operationId: getPlanVersions
      parameters:
        - name: id
          in: path
          required: true
          description: Plan ID
          schema:
            type: string
      responses:
        '200':
          description: Version history retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PlanVersionsResponse'
              example:
                plan_id: plan_1705312200_abc123
                versions:
                  - version: 1
                    changed_at: '2026-01-15T10:00:00Z'
                    change_type: created
                    change_summary: Plan created
                  - version: 2
                    changed_at: '2026-01-15T10:05:00Z'
                    changed_by: user-123
                    change_type: updated
                    change_summary: Changed execution_mode to parallel
        '404':
          description: Plan not found
  /api/v1/plan/{id}/resume:
    post:
      tags:
        - Multi-Agent Planning
      summary: Resume a paused plan (Enterprise only)
      description: >
        Resume execution of a plan that is paused at an approval gate.

        Used with `confirm` and `step` execution modes.


        - `confirm` mode: Every step requires explicit approval

        - `step` mode: First step auto-executes, subsequent steps require
        approval


        Set `approved: true` to approve and execute the next step,

        or `approved: false` to reject and abort the plan.


        **Requires:** Enterprise license.
      operationId: resumePlan
      parameters:
        - name: id
          in: path
          required: true
          description: Plan ID to resume
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                approved:
                  type: boolean
                  description: Whether to approve the pending step
                  default: true
            example:
              approved: true
      responses:
        '200':
          description: Plan resumed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResumePlanResponse'
              examples:
                awaitingNext:
                  summary: Step approved, waiting at next step
                  value:
                    plan_id: plan_1705312200_abc123
                    status: awaiting_approval
                    result: null
                completed:
                  summary: All steps complete
                  value:
                    plan_id: plan_1705312200_abc123
                    status: completed
                    result:
                      summary: Trip booked successfully
        '403':
          description: Enterprise license required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error: Confirm/step execution modes require an Enterprise license
        '404':
          description: Plan not found or not awaiting approval
  /api/v1/plan/{id}/rollback/{version}:
    post:
      tags:
        - Multi-Agent Planning
      summary: Rollback plan to a previous version (Enterprise only)
      description: >
        Rollback a plan to a previously saved version. This creates a new
        version

        that restores the plan state from the specified historical version.


        Uses the plan's version history to retrieve the target version and
        applies

        it as the current state. Returns 409 if a concurrent modification
        occurred.


        **Requires:** Enterprise license.
      operationId: rollbackPlan
      parameters:
        - name: id
          in: path
          required: true
          description: Plan ID to rollback
          schema:
            type: string
          example: plan_1705312200_abc123
        - name: version
          in: path
          required: true
          description: Target version number to rollback to
          schema:
            type: integer
          example: 2
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
      responses:
        '200':
          description: Plan rolled back successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RollbackPlanResponse'
              example:
                plan_id: plan_1705312200_abc123
                version: 4
                previous_version: 2
                status: pending
        '403':
          description: Enterprise license required (community mode)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error: Plan rollback requires an Enterprise license
        '404':
          description: Plan or version not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error: 'Plan not found: plan_nonexistent'
        '409':
          description: Version conflict (concurrent modification)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error: 'Version conflict: plan was modified during rollback'
  /api/v1/agents:
    get:
      tags:
        - Agents
      summary: List all agents (Enterprise)
      description: |
        Returns a paginated list of all agents from the registry.
        In hybrid mode, includes both file-based and database-backed agents.
        Database agents take priority over file agents with the same name.

        **Enterprise only.** The entire `/api/v1/agents` route family is
        registered only in Enterprise builds with a database connection;
        Community deployments return 404 for every `/api/v1/agents` path.
      operationId: listAgents
      parameters:
        - name: page
          in: query
          description: Page number (1-based)
          schema:
            type: integer
            default: 1
            minimum: 1
        - name: page_size
          in: query
          description: Number of agents per page
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
        - name: domain
          in: query
          description: Filter by domain
          schema:
            type: string
      responses:
        '200':
          description: List of agents
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentListResponse'
              example:
                agents:
                  - id: travel/flight-booking
                    name: flight-booking
                    domain: travel
                    description: Flight search and booking agent
                    version: 1
                    is_active: true
                  - id: healthcare/patient-assistant
                    name: patient-assistant
                    domain: healthcare
                    description: Patient query assistant
                    version: 2
                    is_active: true
                pagination:
                  page: 1
                  page_size: 20
                  total: 2
                  total_pages: 1
    post:
      tags:
        - Agents
      summary: Create new agent (Enterprise)
      description: |
        Create a new agent configuration in the database.
        **Enterprise only** - requires database-backed storage.

        The agent is created with version 1 and marked as active by default.
        A version history entry is automatically created.
      operationId: createAgent
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAgentRequest'
            example:
              name: travel-planner
              domain: travel
              description: Travel planning and booking assistant
              is_active: true
              config:
                execution:
                  default_mode: auto
                  max_parallel_tasks: 5
                  timeout_seconds: 300
                agents:
                  - name: flight-search
                    type: llm-call
                    llm:
                      provider: anthropic
                      model: claude-sonnet-4
                routing:
                  - pattern: flight|fly
                    agent: flight-search
                    priority: 10
      responses:
        '201':
          description: Agent created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentResponse'
        '400':
          description: Invalid request or validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Agent with same name already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/agents/{id}:
    get:
      tags:
        - Agents
      summary: Get agent by ID (Enterprise)
      description: >
        Returns detailed information about a specific agent.

        The ID format is `domain/name` (e.g., `travel/flight-booking`).


        **Enterprise only** - the `/api/v1/agents` family is not registered in
        Community.
      operationId: getAgent
      parameters:
        - name: id
          in: path
          required: true
          description: Agent ID in format domain/name
          schema:
            type: string
          example: travel/flight-booking
      responses:
        '200':
          description: Agent details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentResource'
        '404':
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    put:
      tags:
        - Agents
      summary: Update agent (Enterprise)
      description: |
        Update an existing agent configuration.
        **Enterprise only** - requires database-backed storage.

        Updates increment the version number automatically.
        A version history entry is created for the change.
      operationId: updateAgent
      parameters:
        - name: id
          in: path
          required: true
          description: Agent ID (UUID)
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateAgentRequest'
      responses:
        '200':
          description: Agent updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentResponse'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - Agents
      summary: Delete agent (Enterprise)
      description: |
        Delete an agent configuration.
        **Enterprise only** - requires database-backed storage.

        The deletion is recorded in the version history before removal.
      operationId: deleteAgent
      parameters:
        - name: id
          in: path
          required: true
          description: Agent ID (UUID)
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: Agent deleted
        '404':
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/agents/validate:
    post:
      tags:
        - Agents
      summary: Validate agent configuration (Enterprise)
      description: >
        Validates an agent configuration without creating it.

        Useful for dry-run validation before deployment.


        **Enterprise only** - the `/api/v1/agents` family is not registered in
        Community.


        Checks:

        - Required fields present

        - Name/domain format valid

        - Agent types valid (llm-call, connector-call)

        - LLM config complete for llm-call agents

        - Routing rules reference defined agents
      operationId: validateAgent
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ValidateAgentRequest'
      responses:
        '200':
          description: Validation result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResponse'
              example:
                valid: true
                errors: []
        '400':
          description: Validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResponse'
              example:
                valid: false
                errors:
                  - field 'name' is required
                  - routing rule references undefined agent 'unknown-agent'
  /api/v1/agents/{id}/activate:
    post:
      tags:
        - Agents
      summary: Activate agent (Enterprise)
      description: |
        Activate a deactivated agent.
        **Enterprise only** - requires database-backed storage.
      operationId: activateAgent
      parameters:
        - name: id
          in: path
          required: true
          description: Agent ID (UUID)
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Agent activated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentResponse'
        '404':
          description: Agent not found
  /api/v1/agents/{id}/deactivate:
    post:
      tags:
        - Agents
      summary: Deactivate agent (Enterprise)
      description: |
        Deactivate an agent without deleting it.
        **Enterprise only** - requires database-backed storage.
      operationId: deactivateAgent
      parameters:
        - name: id
          in: path
          required: true
          description: Agent ID (UUID)
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Agent deactivated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentResponse'
        '404':
          description: Agent not found
  /api/v1/agents/{id}/test:
    post:
      tags:
        - Agents
      summary: Test agent in sandbox (Enterprise)
      description: |
        Test an agent configuration in a sandbox environment.
        **Enterprise only** - requires database-backed storage.

        Executes a test query against the agent without affecting production.
      operationId: testAgent
      parameters:
        - name: id
          in: path
          required: true
          description: Agent ID (UUID)
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TestAgentRequest'
            example:
              query: Search for flights from NYC to LAX
              context:
                departure_date: '2025-01-15'
      responses:
        '200':
          description: Test result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TestAgentResponse'
        '404':
          description: Agent not found
  /api/v1/agents/{id}/versions:
    get:
      tags:
        - Agents
      summary: Get agent version history (Enterprise)
      description: |
        Returns the version history for an agent.
        **Enterprise only** - requires database-backed storage.

        Includes all changes: create, update, delete, activate, deactivate.
      operationId: getAgentVersions
      parameters:
        - name: id
          in: path
          required: true
          description: Agent ID (UUID)
          schema:
            type: string
            format: uuid
        - name: limit
          in: query
          description: Maximum number of versions to return
          schema:
            type: integer
            default: 50
            maximum: 100
      responses:
        '200':
          description: Version history
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentVersionsResponse'
              example:
                versions:
                  - version: 2
                    change_type: update
                    changed_at: '2025-12-07T12:00:00Z'
                    change_summary: Updated routing rules
                  - version: 1
                    change_type: create
                    changed_at: '2025-12-06T10:00:00Z'
                    change_summary: Initial creation
        '404':
          description: Agent not found
  /api/v1/providers/status:
    get:
      tags:
        - LLM Providers
      summary: Get LLM provider status
      description: Returns status and availability of all configured LLM providers
      operationId: getProviderStatus
      responses:
        '200':
          description: Provider status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProviderStatusResponse'
              example:
                providers:
                  - name: openai
                    available: true
                    models:
                      - gpt-4
                      - gpt-4o
                      - gpt-4o-mini
                    weight: 0.4
                    avg_latency_ms: 1200
                  - name: bedrock
                    available: true
                    models:
                      - anthropic.claude-v2
                      - amazon.titan-text
                    weight: 0.4
                    avg_latency_ms: 900
                  - name: ollama
                    available: true
                    models:
                      - llama3.2
                      - mistral
                    weight: 0.2
                    avg_latency_ms: 500
  /api/v1/providers/weights:
    put:
      tags:
        - LLM Providers
      summary: Update provider routing weights
      description: |
        Update the routing weights for LLM providers.
        Weights determine the probability of routing to each provider.
        Weights must sum to 1.0.
      operationId: updateProviderWeights
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties:
                type: number
                minimum: 0
                maximum: 1
            example:
              openai: 0.5
              bedrock: 0.3
              ollama: 0.2
      responses:
        '200':
          description: Weights updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  message:
                    type: string
              example:
                status: success
                message: Provider weights updated
        '400':
          $ref: '#/components/responses/BadRequest'
  /api/v1/llm-provider-types:
    get:
      tags:
        - LLM Providers
      summary: List available provider types
      description: >
        Returns a list of available LLM provider types (factory info).

        This endpoint helps clients discover what provider types can be
        configured.
      operationId: listLLMProviderTypes
      responses:
        '200':
          description: List of available provider types
          content:
            application/json:
              schema:
                type: object
                properties:
                  provider_types:
                    type: array
                    items:
                      type: object
                      properties:
                        type:
                          type: string
                          enum:
                            - openai
                            - azure-openai
                            - anthropic
                            - bedrock
                            - ollama
                            - gemini
                            - custom
                        name:
                          type: string
                          description: Human-readable name
                        description:
                          type: string
                        supports_streaming:
                          type: boolean
                        requires_api_key:
                          type: boolean
                        configuration_schema:
                          type: object
                          description: JSON Schema for provider configuration
              example:
                provider_types:
                  - type: openai
                    name: OpenAI
                    description: OpenAI GPT models (gpt-4, gpt-4o-mini)
                    supports_streaming: true
                    requires_api_key: true
                  - type: anthropic
                    name: Anthropic
                    description: Anthropic Claude models
                    supports_streaming: true
                    requires_api_key: true
                  - type: bedrock
                    name: AWS Bedrock
                    description: AWS Bedrock models (Claude, Titan, Llama)
                    supports_streaming: true
                    requires_api_key: false
                  - type: ollama
                    name: Ollama
                    description: Local Ollama models
                    supports_streaming: true
                    requires_api_key: false
        '401':
          $ref: '#/components/responses/Unauthorized'
  /api/v1/llm-providers:
    get:
      tags:
        - LLM Providers
      summary: List LLM providers
      description: |
        Returns a paginated list of configured LLM providers.
        Supports filtering by type and enabled status.
      operationId: listLLMProviders
      parameters:
        - name: type
          in: query
          description: Filter by provider type
          schema:
            type: string
            enum:
              - openai
              - azure-openai
              - anthropic
              - bedrock
              - ollama
              - gemini
              - custom
        - name: enabled
          in: query
          description: Filter by enabled status
          schema:
            type: boolean
        - name: page
          in: query
          description: Page number (1-indexed)
          schema:
            type: integer
            default: 1
        - name: page_size
          in: query
          description: Items per page
          schema:
            type: integer
            default: 20
            maximum: 100
      responses:
        '200':
          description: List of LLM providers
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LLMProviderListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags:
        - LLM Providers
      summary: Create LLM provider
      description: |
        Register a new LLM provider. API keys can be provided directly
        or via AWS Secrets Manager ARN for secure credential storage.
      operationId: createLLMProvider
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateLLMProviderRequest'
      responses:
        '201':
          description: Provider created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LLMProviderResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          description: Provider already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LLMProviderAPIError'
  /api/v1/llm-providers/{name}:
    get:
      tags:
        - LLM Providers
      summary: Get LLM provider
      description: Returns details for a specific LLM provider
      operationId: getLLMProvider
      parameters:
        - name: name
          in: path
          required: true
          description: Provider name
          schema:
            type: string
      responses:
        '200':
          description: Provider details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LLMProviderResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Provider not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LLMProviderAPIError'
    put:
      tags:
        - LLM Providers
      summary: Update LLM provider
      description: |
        Update an existing LLM provider configuration.
        Only provided fields are updated (partial update).
      operationId: updateLLMProvider
      parameters:
        - name: name
          in: path
          required: true
          description: Provider name
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateLLMProviderRequest'
      responses:
        '200':
          description: Provider updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LLMProviderResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Provider not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LLMProviderAPIError'
    delete:
      tags:
        - LLM Providers
      summary: Delete LLM provider
      description: Remove an LLM provider configuration
      operationId: deleteLLMProvider
      parameters:
        - name: name
          in: path
          required: true
          description: Provider name
          schema:
            type: string
      responses:
        '204':
          description: Provider deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Provider not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LLMProviderAPIError'
  /api/v1/llm-providers/{name}/health:
    get:
      tags:
        - LLM Providers
      summary: Get provider health
      description: Check health status of a specific LLM provider
      operationId: getLLMProviderHealth
      parameters:
        - name: name
          in: path
          required: true
          description: Provider name
          schema:
            type: string
      responses:
        '200':
          description: Provider health status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LLMProviderHealthResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Provider not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LLMProviderAPIError'
  /api/v1/llm-providers/{name}/test:
    post:
      tags:
        - LLM Providers
      summary: Test LLM provider connection
      description: |
        Tests a provider connection by making a simple API call.
        Returns success status and latency information.
      operationId: testLLMProvider
      parameters:
        - name: name
          in: path
          required: true
          description: Provider name
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                prompt:
                  type: string
                  description: Optional test prompt (default is simple greeting)
                  example: Say hello in one word.
                model:
                  type: string
                  description: Optional model to test with
      responses:
        '200':
          description: Provider test result
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  provider:
                    type: string
                  latency_ms:
                    type: number
                  response:
                    type: string
                    description: Model response (if successful)
                  error:
                    type: string
                    description: Error message (if failed)
              example:
                success: true
                provider: openai
                latency_ms: 245
                response: Hello!
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Provider not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LLMProviderAPIError'
        '503':
          description: Provider connection failed
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: false
                  error:
                    type: string
                    example: Connection timeout
  /api/v1/llm-providers/status:
    get:
      tags:
        - LLM Providers
      summary: Get all providers status
      description: |
        Returns status information for all configured LLM providers.
        Includes enabled status, configuration summary, and last health check.
      operationId: getAllLLMProvidersStatus
      responses:
        '200':
          description: All providers status
          content:
            application/json:
              schema:
                type: object
                properties:
                  providers:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        type:
                          type: string
                        enabled:
                          type: boolean
                        healthy:
                          type: boolean
                        last_check:
                          type: string
                          format: date-time
                        models_count:
                          type: integer
              example:
                providers:
                  - name: openai-primary
                    type: openai
                    enabled: true
                    healthy: true
                    last_check: '2025-01-03T10:30:00Z'
                    models_count: 5
                  - name: anthropic-backup
                    type: anthropic
                    enabled: true
                    healthy: true
                    last_check: '2025-01-03T10:30:00Z'
                    models_count: 3
        '401':
          $ref: '#/components/responses/Unauthorized'
  /api/v1/llm-providers/routing:
    get:
      tags:
        - LLM Providers
      summary: Get routing configuration
      description: Get current LLM provider routing weights
      operationId: getLLMRoutingConfig
      responses:
        '200':
          description: Routing configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LLMRoutingConfigResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
    put:
      tags:
        - LLM Providers
      summary: Update routing weights
      description: |
        Update LLM provider routing weights.
        Weights are integers representing relative priority.
      operationId: updateLLMRoutingWeights
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateLLMRoutingRequest'
      responses:
        '200':
          description: Routing weights updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LLMRoutingConfigResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /api/v1/policies:
    get:
      tags:
        - Dynamic Policies
      summary: List policies
      description: |
        Returns a paginated list of policies with filtering support.
        Use this for policy management in the Customer Portal.
      operationId: listPolicies
      parameters:
        - name: X-Tenant-ID
          in: header
          required: true
          schema:
            type: string
        - name: type
          in: query
          description: Filter by policy type
          schema:
            type: string
            enum:
              - static
              - dynamic
        - name: enabled
          in: query
          description: Filter by enabled status
          schema:
            type: boolean
        - name: search
          in: query
          description: Search in name and description
          schema:
            type: string
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: page_size
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
        - name: sort_by
          in: query
          schema:
            type: string
            enum:
              - name
              - created_at
              - updated_at
              - priority
        - name: sort_dir
          in: query
          schema:
            type: string
            enum:
              - asc
              - desc
            default: asc
      responses:
        '200':
          description: List of policies
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PoliciesListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags:
        - Dynamic Policies
      summary: Create policy
      description: Create a new policy
      operationId: createPolicy
      parameters:
        - name: X-Tenant-ID
          in: header
          required: true
          schema:
            type: string
        - name: X-User-ID
          in: header
          required: false
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePolicyRequest'
      responses:
        '201':
          description: Policy created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Policy'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /api/v1/policies/import:
    post:
      tags:
        - Dynamic Policies
      summary: Import policies
      description: |
        Bulk import policies from JSON or YAML format.
        Supports create or update semantics based on policy_id.
      operationId: importPolicies
      parameters:
        - name: X-Tenant-ID
          in: header
          required: true
          schema:
            type: string
        - name: X-User-ID
          in: header
          required: false
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - policies
              properties:
                policies:
                  type: array
                  items:
                    $ref: '#/components/schemas/CreatePolicyRequest'
                mode:
                  type: string
                  enum:
                    - create
                    - upsert
                  default: upsert
                  description: Import mode - create only or upsert (create or update)
      responses:
        '200':
          description: Import result
          content:
            application/json:
              schema:
                type: object
                properties:
                  created:
                    type: integer
                  updated:
                    type: integer
                  failed:
                    type: integer
                  errors:
                    type: array
                    items:
                      type: string
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /api/v1/policies/export:
    get:
      tags:
        - Dynamic Policies
      summary: Export policies
      description: Export all policies in JSON or YAML format for backup or migration
      operationId: exportPolicies
      parameters:
        - name: X-Tenant-ID
          in: header
          required: true
          schema:
            type: string
        - name: format
          in: query
          description: Export format
          schema:
            type: string
            enum:
              - json
              - yaml
            default: json
        - name: type
          in: query
          description: Filter by policy type
          schema:
            type: string
            enum:
              - static
              - dynamic
              - all
            default: all
      responses:
        '200':
          description: Exported policies
          content:
            application/json:
              schema:
                type: object
                properties:
                  version:
                    type: string
                    example: '1.0'
                  exported_at:
                    type: string
                    format: date-time
                  policies:
                    type: array
                    items:
                      $ref: '#/components/schemas/Policy'
            application/x-yaml:
              schema:
                type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
  /api/v1/policies/{id}:
    get:
      tags:
        - Dynamic Policies
      summary: Get policy by ID
      operationId: getPolicy
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: X-Tenant-ID
          in: header
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Policy details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Policy'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Policy not found
    put:
      tags:
        - Dynamic Policies
      summary: Update policy
      operationId: updatePolicy
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: X-Tenant-ID
          in: header
          required: true
          schema:
            type: string
        - name: X-User-ID
          in: header
          required: false
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdatePolicyRequest'
      responses:
        '200':
          description: Policy updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Policy'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Policy not found
    delete:
      tags:
        - Dynamic Policies
      summary: Delete policy
      operationId: deletePolicy
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: X-Tenant-ID
          in: header
          required: true
          schema:
            type: string
        - name: X-User-ID
          in: header
          required: false
          schema:
            type: string
      responses:
        '204':
          description: Policy deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Policy not found
  /api/v1/policies/{id}/test:
    post:
      tags:
        - Dynamic Policies
      summary: Test policy against input
      description: Test how a specific policy evaluates against sample input
      operationId: testPolicyById
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: X-Tenant-ID
          in: header
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - query
              properties:
                query:
                  type: string
                  description: Input to evaluate
                context:
                  type: object
                  description: Additional context
      responses:
        '200':
          description: Test result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PolicyEvaluationResult'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Policy not found
  /api/v1/policies/{id}/versions:
    get:
      tags:
        - Dynamic Policies
      summary: Get policy version history
      description: |
        Returns version history for a policy.
        Community edition limited to 5 versions.
      operationId: getPolicyVersions
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: X-Tenant-ID
          in: header
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Version history
          content:
            application/json:
              schema:
                type: object
                properties:
                  policy_id:
                    type: string
                  versions:
                    type: array
                    items:
                      type: object
                      properties:
                        version:
                          type: integer
                        snapshot:
                          type: object
                        change_type:
                          type: string
                        changed_by:
                          type: string
                        changed_at:
                          type: string
                          format: date-time
                  count:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Policy not found
  /api/v1/policies/dynamic:
    get:
      tags:
        - Dynamic Policies
      summary: List active dynamic policies
      description: |
        Returns the active dynamic policies visible to the CALLING tenant:
        the tenant's own policies plus the shared global/default baseline.
        Policies owned by other tenants are never returned in the same
        response, and requests without a resolvable tenant are rejected
        with 401 (fail closed).

        The tenant scope is read from the `X-Tenant-ID` header. When the
        request arrives through the AxonFlow Agent, that header is set from
        the validated credential and overwrites any client-supplied value,
        so the caller cannot choose the scope. The orchestrator itself does
        not authenticate this route, so a caller with direct network access
        to the orchestrator can name a tenant — deploy the orchestrator on a
        private network behind the Agent.
      operationId: listDynamicPolicies
      responses:
        '200':
          description: List of dynamic policies visible to the calling tenant
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/DynamicPolicy'
              example:
                - id: pol_001
                  name: High Risk Content Filter
                  description: Block requests with high risk scores
                  enabled: true
                  conditions:
                    - field: risk_score
                      operator: '>'
                      value: 0.8
                  actions:
                    - type: block
                      reason: High risk content detected
                - id: pol_002
                  name: Rate Limit Premium Users
                  description: Apply premium rate limits
                  enabled: true
                  conditions:
                    - field: user.role
                      operator: '=='
                      value: premium
                  actions:
                    - type: rate_limit
                      limit: 10000
        '401':
          description: >-
            Tenant scope could not be resolved (missing gateway-stamped
            X-Tenant-ID); the endpoint fails closed and returns no policy data
  /api/v1/policies/test:
    post:
      tags:
        - Dynamic Policies
      summary: Test policy evaluation
      description: Test how dynamic policies evaluate a sample request
      operationId: testPolicy
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - query
              properties:
                query:
                  type: string
                user:
                  $ref: '#/components/schemas/UserContext'
                request_type:
                  type: string
            example:
              query: SELECT * FROM customers WHERE credit_score < 500
              user:
                id: 123
                email: analyst@company.com
                role: analyst
              request_type: sql
      responses:
        '200':
          description: Policy evaluation result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PolicyEvaluationResult'
              example:
                allowed: true
                applied_policies:
                  - sql-injection-filter
                  - pii-protection
                risk_score: 0.25
                required_actions: []
                processing_time_ms: 3
  /api/v1/metrics:
    get:
      tags:
        - Metrics
      summary: Get detailed metrics
      description: Returns detailed metrics from the metrics collector
      operationId: getDetailedMetrics
      responses:
        '200':
          description: Detailed metrics
          content:
            application/json:
              schema:
                type: object
  /api/v1/audit/search:
    post:
      tags:
        - Audit
      summary: Search audit logs
      description: |
        Search audit logs by various criteria. The tenant scope is always forced
        from the `X-Tenant-ID` header (the body cannot override it); requests
        without the header are rejected with 401. `user_email` and `client_id`
        are case-insensitive partial (ILIKE substring) matches. The search
        start time is clamped to the tenant's tier-based retention window.

        **Role-scoped reads (#2922):** the caller's read scope is resolved
        server-side. `admin`/`owner` read the full tenant trail; every other
        role — and any caller without a validated per-user identity — reads
        **only their own** `user_email` rows (fail-closed). A non-admin's
        `user_email` filter can only narrow the result to their own identity,
        never widen it to another user's rows. Callers without any resolvable
        identity receive an empty `entries` array. The role/scope is trusted
        only over the internal agent→orchestrator proxy-auth channel, never a
        client-forwarded header.

        **Single-operator deployments (#3060):** `DEPLOYMENT_MODE=community`
        reads tenant-wide unconditionally, and `DEPLOYMENT_MODE=community-saas`
        reads tenant-wide for requests that arrived over the agent gateway
        (proven by the internal proxy-auth token) — in that mode the
        organization, tenant and credential are one `cs_<uuid>`, so tenant-wide
        is that single evaluator's own data. A community-saas request that
        reaches the orchestrator directly stays least-privilege. Read scope is
        a separate axis from administrative authority: a community-saas caller
        reads tenant-wide here and is still denied (403) the whole-tenant
        compliance exports and the cost/usage/execution family.

        **Method:** this endpoint is **POST-only** (its criteria are a JSON
        body). A `GET` returns **405** with `Allow: POST, OPTIONS`.
      operationId: searchAuditLogs
      parameters:
        - $ref: '#/components/parameters/TenantIDHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuditSearchRequest'
            example:
              user_email: analyst@company.com
              client_id: analytics-app
              start_time: '2025-01-01T00:00:00Z'
              end_time: '2025-01-15T23:59:59Z'
              action: blocked
              session_id: sess-4f6a2c
              limit: 100
      responses:
        '200':
          description: |
            Audit search results. `total` is the true pre-LIMIT match count
            for the filters, so callers can paginate with `limit`/`offset`.
            `entries` is always a JSON array (`[]` when empty, never `null`).
          headers:
            X-Axonflow-Read-Scope:
              $ref: '#/components/headers/XAxonflowReadScope'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuditSearchResponse'
              example:
                entries:
                  - id: a1b2c3d4
                    request_id: req_9f8e7d
                    timestamp: '2025-01-14T10:30:00Z'
                    user_email: analyst@company.com
                    tenant_id: tenant-abc
                    request_type: llm_chat
                    policy_decision: blocked
                    session_id: sess-4f6a2c
                total: 235
                limit: 100
                offset: 0
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing X-Tenant-ID header (tenant scoping required)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Audit search failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/audit/summary:
    post:
      tags:
        - Audit
      summary: Get audit compliance summary
      description: |
        Returns aggregated compliance summary statistics for a given date range.
        Includes total event counts, breakdowns by severity and action type,
        top triggered policies, and an overall compliance score.
      operationId: getAuditSummary
      parameters:
        - name: X-Tenant-ID
          in: header
          required: true
          description: >
            Tenant identifier for scoping results. Required to prevent
            cross-tenant

            data aggregation. There is no fallback header: the X-Org-ID fallback
            was

            removed in v6.2.0, and a request without X-Tenant-ID is rejected
            with 400.
          schema:
            type: string
          example: travel-us
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - start_time
                - end_time
              properties:
                start_time:
                  type: string
                  format: date-time
                  description: Start of date range (RFC3339)
                end_time:
                  type: string
                  format: date-time
                  description: End of date range (RFC3339, must be after start_time)
            example:
              start_time: '2026-01-01T00:00:00Z'
              end_time: '2026-04-01T00:00:00Z'
      responses:
        '200':
          description: |
            Compliance summary. A fail-closed read (`X-Axonflow-Read-Scope:
            none`) returns the zero-events summary, whose `compliance_score`
            of 100 reads as "all clear" — the header is what tells the two
            apart.
          headers:
            X-Axonflow-Read-Scope:
              $ref: '#/components/headers/XAxonflowReadScope'
          content:
            application/json:
              schema:
                type: object
                properties:
                  total_events:
                    type: integer
                    description: Total audit events in the date range
                  by_severity:
                    type: object
                    additionalProperties:
                      type: integer
                    description: Event counts by severity (info, warning, critical)
                  by_action:
                    type: object
                    additionalProperties:
                      type: integer
                    description: Event counts by action type (llm_call, tool_call, etc.)
                  top_policies:
                    type: array
                    items:
                      type: object
                      properties:
                        policy_name:
                          type: string
                        trigger_count:
                          type: integer
                        block_count:
                          type: integer
                    description: Top 10 policies by trigger count
                  compliance_score:
                    type: number
                    format: float
                    description: Compliance score 0-100 (100 = no blocked events)
              example:
                total_events: 1523
                by_severity:
                  info: 1400
                  warning: 100
                  critical: 23
                by_action:
                  llm_call: 1200
                  tool_call: 300
                  policy_check: 23
                top_policies:
                  - policy_name: demo-block-bulk-email
                    trigger_count: 15
                    block_count: 15
                  - policy_name: pii-detection
                    trigger_count: 8
                    block_count: 3
                compliance_score: 98.5
        '400':
          description: |
            Invalid request (bad/missing RFC3339 timestamps, end_time not after
            start_time, range over 1 year) or missing X-Tenant-ID header.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
  /api/v1/audit/tenant/{tenant_id}:
    get:
      tags:
        - Audit
      summary: Get tenant audit logs
      description: |
        Get recent audit logs for a specific tenant. The URL tenant must match
        the session tenant carried in the `X-Tenant-ID` header: a missing header
        is rejected with 401 (fail-closed), and a mismatch with 403. Results are
        clamped to the tenant's tier-based retention window.
      operationId: getTenantAuditLogs
      parameters:
        - name: tenant_id
          in: path
          required: true
          description: Tenant identifier (must equal the X-Tenant-ID header value)
          schema:
            type: string
          example: tenant-abc
        - $ref: '#/components/parameters/TenantIDHeader'
        - name: limit
          in: query
          required: false
          description: Maximum number of rows to return (1-1000)
          schema:
            type: integer
            minimum: 1
            maximum: 1000
            default: 50
        - name: page_size
          in: query
          required: false
          description: |
            Deprecated alias for `limit` (1-1000); ignored when `limit` is
            supplied. Kept for backward compatibility.
          deprecated: true
          schema:
            type: integer
            minimum: 1
            maximum: 1000
      responses:
        '200':
          description: Tenant audit logs
          headers:
            X-Axonflow-Read-Scope:
              $ref: '#/components/headers/XAxonflowReadScope'
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AuditLogEntry'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          description: Missing X-Tenant-ID header
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: URL tenant does not match the session tenant (tenant scope mismatch)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/audit/tool-call:
    post:
      tags:
        - Audit
      summary: Record a tool call audit entry
      description: >
        Records a non-LLM tool call (API calls, webhooks, MCP tool executions by
        external

        orchestrators) in the AxonFlow audit trail. Only `tool_name` is
        required; all other

        fields are optional.
      operationId: auditToolCall
      security:
        - basicAuth: []
      parameters:
        - name: X-Tenant-ID
          in: header
          required: true
          description: >-
            Tenant identifier (must match the client ID from Basic auth
            credentials)
          schema:
            type: string
        - name: X-Axonflow-Proxy-Auth
          in: header
          required: true
          description: >
            Internal-service HMAC token proving the request was routed through
            the

            AxonFlow Agent gateway (derived from
            `AXONFLOW_INTERNAL_SERVICE_SECRET`).

            Enforced **fail-closed in every non-Community deployment**: a
            missing or

            invalid token — or an unconfigured secret — is rejected with 403, in

            addition to the Basic auth requirement below. Community deployments

            without the secret configured skip this check.
          schema:
            type: string
        - name: Idempotency-Key
          in: header
          required: false
          description: |
            Optional per-request dedup token. When supplied, the platform caches
            the response for 24h and returns it byte-for-byte on subsequent
            requests carrying the same key + same authenticated tenant. A cache
            hit adds an `Idempotent-Replayed: true` response header. Pattern:
            `^[A-Za-z0-9_.:\-/]+$`, max 256 chars. 5xx responses are not cached
            so the caller's retry can hit a fresh attempt.
          schema:
            type: string
            minLength: 1
            maxLength: 256
            pattern: ^[A-Za-z0-9_.:\-/]+$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuditToolCallRequest'
            example:
              tool_name: getUserInfo
              caller_name: claude_code
              input: {}
              output: {}
              workflow_id: wf_abc123
              step_id: step-3
              user_id: user@example.com
              duration_ms: 45
              policies_applied:
                - pii_check
                - data_access
              success: true
              error_message: ''
      responses:
        '201':
          description: Tool call audit recorded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuditToolCallResponse'
              example:
                audit_id: audit_1710432000_abcd1234
                status: recorded
                timestamp: '2026-03-14T12:00:00Z'
        '400':
          description: >-
            Bad request (missing tool_name, invalid body, or missing
            X-Tenant-ID)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing or invalid Basic auth credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: >-
            Client ID does not match tenant scope, or request not routed through
            Agent gateway
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/audit/export:
    post:
      tags:
        - Audit
      summary: Export audit logs (CSV or JSON)
      description: |
        Exports audit logs as a downloadable file. Filters mirror
        `/api/v1/audit/search` (same ILIKE user/client matching, canonical
        action expansion, JSONB policy filters and date range), so an export
        always reconciles with the on-screen search for the same filters.
        Tenant scope is forced from the `X-Tenant-ID` header.

        The export is capped at **50,000 rows**; when the cap is hit the
        response carries the `X-Audit-Export-Truncated: true` and
        `X-Audit-Export-Row-Cap` headers so callers can warn that the file is
        partial. Free-text CSV cells are formula-escaped (leading `=`, `+`,
        `-`, `@`, tab or CR is prefixed with `'`) to neutralize spreadsheet
        formula injection.
      operationId: exportAuditLogs
      parameters:
        - $ref: '#/components/parameters/TenantIDHeader'
        - name: format
          in: query
          required: false
          description: Export format. Defaults to `json`.
          schema:
            type: string
            enum:
              - csv
              - json
            default: json
      requestBody:
        required: false
        description: |
          Optional filters. An empty body exports the whole tenant window
          (within the tier retention floor). A present-but-malformed body is a
          400.
        content:
          application/json:
            schema:
              type: object
              properties:
                user_email:
                  type: string
                  description: Case-insensitive partial (ILIKE substring) match
                client_id:
                  type: string
                  description: Case-insensitive partial (ILIKE substring) match
                action:
                  type: string
                  description: |
                    Canonical verdict filter (allowed, blocked, redacted,
                    needs_approval, error); expanded to all historical DB
                    spellings of that verdict.
                session_id:
                  type: string
                  description: Exact match on the first-class session_id column
                decision_id:
                  type: string
                  description: Matches policy_details->>'decision_id'
                policy_name:
                  type: string
                  description: >-
                    Same three-shape policy_details match as
                    /api/v1/audit/search
                override_id:
                  type: string
                  description: Matches policy_details->>'override_id'
                start_time:
                  type: string
                  format: date-time
                end_time:
                  type: string
                  format: date-time
            example:
              action: blocked
              start_time: '2026-06-01T00:00:00Z'
              end_time: '2026-06-30T23:59:59Z'
      responses:
        '200':
          description: |
            Export file (Content-Disposition attachment). CSV columns, in
            order: id, timestamp, user_email, tenant_id, org_id,
            policy_decision, request_type, query, response_sample, provider,
            model, response_time_ms, correlation_id, session_id.
          headers:
            X-Axonflow-Read-Scope:
              $ref: '#/components/headers/XAxonflowReadScope'
            X-Audit-Export-Truncated:
              description: Present (value `true`) when the 50,000-row cap was hit
              schema:
                type: string
            X-Audit-Export-Row-Cap:
              description: >-
                The row cap in effect (50000). Present only when the cap was
                hit, alongside X-Audit-Export-Truncated
              schema:
                type: integer
          content:
            application/json:
              schema:
                type: object
                properties:
                  entries:
                    type: array
                    items:
                      $ref: '#/components/schemas/AuditLogEntry'
                  count:
                    type: integer
                    description: Number of entries in this export
                  truncated:
                    type: boolean
                    description: True when the row cap was hit
                  row_cap:
                    type: integer
                    example: 50000
            text/csv:
              schema:
                type: string
        '400':
          description: Invalid format value or malformed request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing X-Tenant-ID header (tenant scoping required)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Audit export failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: Audit subsystem unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/audit/report:
    post:
      tags:
        - Audit
      summary: Per-action audit report
      description: |
        Aggregates audit logs for a window into per-action counts, average
        latency, and top policies, tenant-scoped and optionally filtered by
        user and a single canonical action. Counts reconcile with
        `/api/v1/audit/search` for the same filters.
      operationId: getAuditActionReport
      parameters:
        - $ref: '#/components/parameters/TenantIDHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - start_time
                - end_time
              properties:
                start_time:
                  type: string
                  format: date-time
                  description: Window start (RFC3339)
                end_time:
                  type: string
                  format: date-time
                  description: >-
                    Window end (RFC3339, must be after start_time; range at most
                    1 year)
                user_email:
                  type: string
                  description: Optional case-insensitive partial match
                action:
                  type: string
                  description: Optional single canonical action filter
            example:
              start_time: '2026-06-01T00:00:00Z'
              end_time: '2026-06-30T23:59:59Z'
      responses:
        '200':
          description: |
            Per-action report. A fail-closed read (`X-Axonflow-Read-Scope:
            none`) returns the seeded all-zeroes report, which has the same
            shape as "no rows in the window".
          headers:
            X-Axonflow-Read-Scope:
              $ref: '#/components/headers/XAxonflowReadScope'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuditActionReport'
              example:
                tenant_id: tenant-abc
                start_time: '2026-06-01T00:00:00Z'
                end_time: '2026-06-30T23:59:59Z'
                total: 1523
                by_action:
                  allowed: 1400
                  blocked: 100
                  redacted: 20
                  needs_approval: 3
                  error: 0
                avg_latency_ms: 245.7
                top_policies:
                  - policy_name: pii-detection
                    trigger_count: 88
                    block_count: 35
        '400':
          description: |
            Invalid body, non-RFC3339 timestamps, end_time not after
            start_time, or range over 1 year
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing X-Tenant-ID header (tenant scoping required)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Audit report failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: Audit subsystem unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/audit/session-summary:
    get:
      tags:
        - Audit
      summary: Session-level usage summary (Enterprise)
      description: |
        **Enterprise** — session-level usage reporting (#2759). The Community
        build mounts the same route but returns **501 Not Implemented**.

        Buckets audit logs into per-session aggregates for the date window:
        a bucket is **per-session** when `session_id` is present on its rows,
        otherwise rows without a session id fall back to a **per-user-day**
        bucket (the `day` field is set instead of `session_id`). Buckets are
        capped at `bucket_limit` (most-recent activity first); `truncated` is
        true when the window held more buckets than the cap — narrow the
        window or raise `limit`. Drill into a bucket's raw events via
        `POST /api/v1/audit/search` with its `session_id` (#2857).

        The window start is clamped to the tenant's tier retention floor,
        like `/api/v1/audit/search` and `/api/v1/audit/export`.
      operationId: getAuditSessionSummary
      parameters:
        - $ref: '#/components/parameters/TenantIDHeader'
        - name: start_date
          in: query
          required: true
          description: Window start, calendar day (YYYY-MM-DD)
          schema:
            type: string
            format: date
          example: '2026-07-01'
        - name: end_date
          in: query
          required: true
          description: Window end, calendar day (YYYY-MM-DD), inclusive
          schema:
            type: string
            format: date
          example: '2026-07-07'
        - name: user_email
          in: query
          required: false
          description: Case-insensitive partial (ILIKE substring) filter
          schema:
            type: string
        - name: limit
          in: query
          required: false
          description: |
            Caps the number of returned buckets. Default 200. Values above the
            server max (1000) are clamped; the effective bound is echoed back
            as `bucket_limit`. A non-positive or non-integer value is a 400.
          schema:
            type: integer
            minimum: 1
            default: 200
      responses:
        '200':
          description: Session summary buckets
          headers:
            X-Axonflow-Read-Scope:
              $ref: '#/components/headers/XAxonflowReadScope'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionSummaryResponse'
        '400':
          description: |
            Missing/invalid start_date or end_date, end_date before
            start_date, range over 1 year, or invalid limit
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing X-Tenant-ID header (tenant scoping required)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Session summary query failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '501':
          description: >-
            Community edition — session summary reporting is an Enterprise
            feature
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: Audit subsystem unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/audit/{id}:
    get:
      tags:
        - Audit
      summary: Get a single audit entry by ID
      description: |
        Returns the full audit entry for the given id, tenant-scoped by the
        `X-Tenant-ID` header. A record that exists but belongs to another
        tenant returns 404 (not 403), so the endpoint cannot be used as a
        cross-tenant existence oracle. Literal `/api/v1/audit/*` routes
        (search, export, report, session-summary, tenant, tool-call) are
        matched before this parameterized path — including `GET
        /api/v1/audit/search`, which answers **405** rather than being
        swallowed here as an id of `"search"` (#3060).

        **Role-scoped reads (#2922):** a non-tenant-wide caller may fetch only
        their own rows; a record belonging to another user returns the same
        404 as a missing one (non-oracle). See
        `POST /api/v1/audit/search` for the deployment-mode carve-outs.
      operationId: getAuditLogById
      parameters:
        - name: id
          in: path
          required: true
          description: Audit entry id
          schema:
            type: string
        - $ref: '#/components/parameters/TenantIDHeader'
      responses:
        '200':
          description: Full audit entry
          headers:
            X-Axonflow-Read-Scope:
              $ref: '#/components/headers/XAxonflowReadScope'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuditLogEntry'
        '400':
          description: Missing audit id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing X-Tenant-ID header (tenant scoping required)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: |
            Audit record not found (or belongs to another tenant, or is
            outside the caller's read scope). Deliberately the same body in
            all three cases so the endpoint is not an existence oracle —
            `X-Axonflow-Read-Scope` is the operator-side channel that tells
            them apart.
          headers:
            X-Axonflow-Read-Scope:
              $ref: '#/components/headers/XAxonflowReadScope'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Audit detail lookup failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: Audit subsystem unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/overrides:
    post:
      tags:
        - Decisions & Overrides
      summary: Create a session-scoped policy override
      description: |
        Creates a time-boxed override for a static or dynamic policy
        (ADR-044). A justification (`override_reason`, max 500 chars) is
        mandatory. TTL is clamped server-side: default 3600s when omitted,
        minimum 60s, hard cap 86400s (24h) — the response reports the
        requested value and the clamp reason when clamping occurred.

        Critical-risk policies and policies with `allow_override=false`
        cannot be overridden (403).
      operationId: createPolicyOverride
      parameters:
        - name: X-User-Email
          in: header
          required: true
          description: >-
            Authenticated user identity (falls back to X-User-ID); missing
            identity is a 401
          schema:
            type: string
        - $ref: '#/components/parameters/TenantIDHeader'
        - $ref: '#/components/parameters/OrgIDHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOverrideRequest'
            example:
              policy_id: pii-us-ssn-redact
              policy_type: static
              tool_signature: mcp:github/create_issue
              override_reason: Approved incident-response exception INC-4432
              ttl_seconds: 1800
      responses:
        '201':
          description: Override created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateOverrideResponse'
        '400':
          description: |
            Invalid body; missing/invalid policy_id, policy_type (must be
            `static` or `dynamic`) or override_reason; or missing X-Tenant-ID
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing user identity (X-User-Email / X-User-ID)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Policy is critical-risk or has allow_override=false
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Policy not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    get:
      tags:
        - Decisions & Overrides
      summary: List policy overrides
      description: |
        Lists the tenant's policy overrides, newest first, capped at 100
        rows. Revoked overrides are excluded unless `include_revoked=true`.

        **Role-scoped reads (#2922):** `admin`/`owner` list every override in
        the tenant; every other role lists only the overrides they created
        (`created_by`). Revoking an override is scoped the same way. The
        role/scope is trusted only over the internal proxy-auth channel.
      operationId: listPolicyOverrides
      parameters:
        - $ref: '#/components/parameters/TenantIDHeader'
        - name: policy_id
          in: query
          required: false
          description: Filter by policy UUID or slug/name
          schema:
            type: string
        - name: include_revoked
          in: query
          required: false
          description: Include revoked overrides (only the literal `true` enables it)
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: Override list
          headers:
            X-Axonflow-Read-Scope:
              $ref: '#/components/headers/XAxonflowReadScope'
          content:
            application/json:
              schema:
                type: object
                properties:
                  overrides:
                    type: array
                    description: Always a JSON array (`[]` when empty)
                    items:
                      $ref: '#/components/schemas/OverrideSummary'
                  count:
                    type: integer
        '400':
          description: Missing X-Tenant-ID header
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/overrides/{id}:
    get:
      tags:
        - Decisions & Overrides
      summary: Get a policy override
      description: |
        Returns one override by id, tenant-scoped. Another tenant's override
        returns 404.
      operationId: getPolicyOverride
      parameters:
        - name: id
          in: path
          required: true
          description: Override id
          schema:
            type: string
        - $ref: '#/components/parameters/TenantIDHeader'
      responses:
        '200':
          description: Override detail
          headers:
            X-Axonflow-Read-Scope:
              $ref: '#/components/headers/XAxonflowReadScope'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OverrideDetail'
        '400':
          description: Missing override id or missing X-Tenant-ID header
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: |
            Override not found — also returned when the override exists but
            was created by another user and the caller is scoped to
            `own-rows`. Same body in both cases (non-oracle);
            `X-Axonflow-Read-Scope` distinguishes them.
          headers:
            X-Axonflow-Read-Scope:
              $ref: '#/components/headers/XAxonflowReadScope'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - Decisions & Overrides
      summary: Revoke a policy override
      description: |
        Revokes an active override (tenant-scoped). An already-revoked or
        cross-tenant override returns 404.
      operationId: revokePolicyOverride
      parameters:
        - name: id
          in: path
          required: true
          description: Override id
          schema:
            type: string
        - name: X-User-Email
          in: header
          required: true
          description: >-
            Authenticated user identity (falls back to X-User-ID); missing
            identity is a 401
          schema:
            type: string
        - $ref: '#/components/parameters/TenantIDHeader'
        - $ref: '#/components/parameters/OrgIDHeader'
      responses:
        '200':
          description: Override revoked
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  revoked_at:
                    type: string
                    format: date-time
        '400':
          description: Missing override id or missing X-Tenant-ID header
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing user identity (X-User-Email / X-User-ID)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Override not found or already revoked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/decisions:
    get:
      tags:
        - Decisions & Overrides
      summary: List recent governance decisions
      description: |
        Lists recent decisions for the tenant, newest first. The lookback
        window and maximum page size are **tier-gated**: Community/Free sees
        the last 5 decisions in 24h; Evaluation 100 decisions in 14 days;
        Enterprise up to 1000 with an unbounded window. Requesting a `limit`
        above the tier cap returns **429** with the upgrade envelope
        (`error`, `limit_type: decision_list_size`, `tier`, `limit`,
        `remaining`, `upgrade{tier, wording, compare_url, buy_url}`) plus the
        `X-Axonflow-Tier-Limit` and `X-Axonflow-Upgrade-URL` headers. A
        `since` earlier than the tier window is silently clamped to the
        window.

        **Role-scoped reads (#2922):** `admin`/`owner` list every user's
        decisions; every other role lists only their own (rows attributed to
        their identity). A caller with no resolvable per-user identity gets an
        empty list. The role/scope is trusted only over the internal
        proxy-auth channel. `DEPLOYMENT_MODE=community` lists tenant-wide, and
        `community-saas` lists tenant-wide over the agent gateway — see
        `POST /api/v1/audit/search` for the single-operator rationale (#3060).
      operationId: listDecisions
      parameters:
        - $ref: '#/components/parameters/TenantIDHeader'
        - name: limit
          in: query
          required: false
          description: Page size (positive integer). Defaults to the tier maximum.
          schema:
            type: integer
            minimum: 1
        - name: since
          in: query
          required: false
          description: >-
            RFC3339 lower bound; defaults to (and is clamped to) the tier
            lookback window
          schema:
            type: string
            format: date-time
        - name: decision
          in: query
          required: false
          description: Canonical verdict filter
          schema:
            type: string
            enum:
              - allowed
              - blocked
              - redacted
              - needs_approval
              - error
        - name: policy_id
          in: query
          required: false
          description: Filter to decisions where this policy fired
          schema:
            type: string
        - name: tool_signature
          in: query
          required: false
          description: Filter by tool signature
          schema:
            type: string
      responses:
        '200':
          description: Decision list (`decisions` is `[]` when empty, never `null`)
          headers:
            X-Axonflow-Read-Scope:
              $ref: '#/components/headers/XAxonflowReadScope'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DecisionListResponse'
        '400':
          description: Invalid limit, since (must be RFC3339), or decision value
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing X-Tenant-ID header
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Requested limit exceeds the tier page cap
          headers:
            X-Axonflow-Tier-Limit:
              schema:
                type: string
                example: decision_list_size
            X-Axonflow-Upgrade-URL:
              schema:
                type: string
                example: https://getaxonflow.com/pricing/
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DecisionListLimitEnvelope'
        '500':
          description: Internal error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/decisions/{id}/explain:
    get:
      tags:
        - Decisions & Overrides
      summary: Explain a governance decision
      description: |
        Returns the explanation for a decision id (ADR-043): matched
        policies, verdict and reason, risk level, override availability (and
        any existing override id), the caller's 24h hit count for the first
        matched policy, and policy version drift (version at decision time vs
        latest). Tenant-scoped via `X-Tenant-ID`; a decision belonging to
        another tenant returns 404 (not 403) so the endpoint is not an
        existence oracle.
      operationId: explainDecision
      parameters:
        - name: id
          in: path
          required: true
          description: Decision id
          schema:
            type: string
        - $ref: '#/components/parameters/TenantIDHeader'
        - name: X-User-Email
          in: header
          required: false
          description: >-
            Caller identity used for the per-user historical hit count (falls
            back to X-User-ID)
          schema:
            type: string
      responses:
        '200':
          description: Decision explanation
          headers:
            X-Axonflow-Read-Scope:
              $ref: '#/components/headers/XAxonflowReadScope'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DecisionExplanation'
        '400':
          description: Missing decision id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing X-Tenant-ID header
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Not authorized to explain this decision (defensive tenant mismatch)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: |
            Decision not found, past the retention window, belongs to another
            tenant, or belongs to another user while the caller is scoped to
            `own-rows`. All four return the same body (non-oracle);
            `X-Axonflow-Read-Scope` is what separates a scoping outcome from a
            genuinely missing decision — the "explain fails on a decision the
            platform produced seconds ago" symptom.
          headers:
            X-Axonflow-Read-Scope:
              $ref: '#/components/headers/XAxonflowReadScope'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/sebi/dashboard:
    get:
      tags:
        - SEBI Compliance
      summary: Get SEBI compliance dashboard
      description: >
        Returns a comprehensive SEBI compliance dashboard including:

        - Overall compliance score and status

        - 5-year retention status

        - PII detection/redaction metrics (PAN, Aadhaar)

        - Policy violation summary with trend

        - HITL review queue


        **Enterprise Feature**: Available only for Indian financial services
        deployments.
      operationId: getSEBIDashboard
      responses:
        '200':
          description: SEBI compliance dashboard data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SEBIDashboard'
              example:
                framework: SEBI_DPDP_COMBINED
                overall_score: 85
                overall_status: COMPLIANT
                last_audit_export: '2024-12-01T10:00:00Z'
                retention_status:
                  org_id: 123
                  framework: SEBI_AI_ML
                  compliance_status: COMPLIANT
                violations_summary:
                  total: 142
                  by_severity:
                    critical: 2
                    high: 15
                    medium: 45
                    low: 80
                  trend: improving
                pii_summary:
                  total_detections: 5420
                  total_redactions: 5350
                  redaction_rate_percent: 98.7
                hitl_reviews_pending: 3
        '403':
          description: Not authorized for SEBI compliance features
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/sebi/audit/export:
    post:
      tags:
        - SEBI Compliance
      summary: Export SEBI audit data
      description: >
        Export audit data for SEBI regulatory submission. Supports:

        - Multiple compliance frameworks (SEBI AI/ML, DPDP, Combined)

        - Various data types (policy violations, LLM calls, decision chain,
        HITL, PII redactions)

        - Multiple export formats (JSON, CSV, XML)

        - Optional PII redaction for external auditors


        Large exports are processed asynchronously. Poll the export status
        endpoint

        to check completion and get the download URL.


        **5-Year Retention**: Per SEBI AI/ML Guidelines, all audit data is
        retained

        for minimum 5 years (1825 days).
      operationId: exportSEBIAuditData
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SEBIAuditExportRequest'
            example:
              start_date: '2024-01-01T00:00:00Z'
              end_date: '2024-12-31T23:59:59Z'
              data_types:
                - policy_violations
                - llm_calls
                - pii_redactions
              format: json
              framework: SEBI_DPDP_COMBINED
              redact_pii: false
      responses:
        '200':
          description: Export started (sync) or queued (async)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SEBIAuditExportResponse'
              example:
                export_id: exp_abc123
                status: processing
                framework: SEBI_DPDP_COMBINED
                metadata:
                  export_version: '1.0'
                  generated_by: axonflow-orchestrator
                  org_id: 123
                  retention_days: 1825
        '400':
          $ref: '#/components/responses/BadRequest'
  /api/v1/sebi/audit/export/{export_id}:
    get:
      tags:
        - SEBI Compliance
      summary: Get export status
      description: |
        Get the status of an asynchronous SEBI audit export.
        When status is "completed", the download_url will be provided.
      operationId: getSEBIExportStatus
      parameters:
        - name: export_id
          in: path
          required: true
          description: Export ID from the export request
          schema:
            type: string
          example: exp_abc123
      responses:
        '200':
          description: Export status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SEBIAuditExportResponse'
              example:
                export_id: exp_abc123
                status: completed
                framework: SEBI_DPDP_COMBINED
                download_url: /api/v1/sebi/audit/export/exp_abc123/download
                expires_at: '2024-12-08T14:00:00Z'
                summary:
                  total_records: 15420
                  compliance_score: 85.5
        '404':
          description: Export not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/sebi/audit/retention:
    get:
      tags:
        - SEBI Compliance
      summary: Get retention status
      description: |
        Get the 5-year retention compliance status for all audit data types.

        SEBI AI/ML Guidelines require:
        - All AI/ML decisions retained for 5 years
        - Audit trail for human oversight
        - Decision chain tracing

        This endpoint reports compliance status for each data type.
      operationId: getSEBIRetentionStatus
      responses:
        '200':
          description: Retention status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SEBIRetentionResponse'
              example:
                org_id: 123
                framework: SEBI_AI_ML
                compliance_status: COMPLIANT
                status:
                  - data_type: policy_violations
                    retention_days: 1825
                    total_records: 15420
                    oldest_record: '2020-01-15T10:30:00Z'
                    compliance_status: COMPLIANT
                  - data_type: llm_calls
                    retention_days: 1825
                    total_records: 250000
                    oldest_record: '2020-06-01T08:00:00Z'
                    compliance_status: COMPLIANT
  /api/v1/sebi/audit/readiness:
    get:
      tags:
        - SEBI Compliance
      summary: Check compliance readiness
      description: |
        Validate organization readiness for SEBI regulatory audit.

        Checks include:
        - Retention configuration (5-year minimum)
        - PII detection policies
        - Human oversight mechanisms
        - Audit logging completeness
        - Decision chain tracing

        Returns a score (0-100) and actionable recommendations.
      operationId: getSEBIComplianceReadiness
      responses:
        '200':
          description: Compliance readiness assessment
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SEBIComplianceReadiness'
              example:
                ready: true
                score: 85
                checks:
                  - name: Retention Configuration
                    status: pass
                  - name: PII Detection Policies
                    status: pass
                  - name: Human Oversight
                    status: pass
                  - name: Decision Chain Tracing
                    status: warning
                    details: Consider enabling for full audit trail
                recommendations:
                  - >-
                    Enable decision chain tracing to maintain full audit trail
                    of AI decisions
  /api/v1/ojk/audit/export:
    post:
      tags:
        - OJK Compliance
      summary: Export OJK audit data
      description: |
        Export audit data for OJK/BI regulatory submission. Supports:
        - Compliance frameworks: `OJK_AI_GOVERNANCE`, `UU_PDP`, `BI_PJP`,
          `OJK_BI_COMBINED` (default)
        - Data types: policy_violations, llm_calls, decision_chain,
          cross_border_transfers, breach_notifications, all (default).
          `hitl_oversight` and `pii_redactions` are accepted by request
          validation but are **not implemented** by the export service —
          requesting them (or expecting them under `all`) yields no rows
          and no error (no `queryHITLRecords`/`queryPIIRedactions` exists
          in `ojk_audit_export_service.go`).
        - Formats: json (default), csv, xml

        The date range may span at most 5 years, matching the OJK 5-year
        (1825-day) retention requirement. The export runs synchronously and
        returns `status: completed` with the data inline.

        **Enterprise Feature**: Available only for Indonesian financial
        services deployments.
      operationId: exportOJKAuditData
      parameters:
        - name: X-Tenant-ID
          in: header
          required: false
          description: |
            Tenant identifier. The OJK module falls back to X-Org-ID when
            absent; a request carrying neither header is rejected with 400
            (code `missing_tenant`).
          schema:
            type: string
        - name: X-Org-ID
          in: header
          required: false
          description: Fallback tenant identifier when X-Tenant-ID is absent
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OJKAuditExportRequest'
            example:
              start_date: '2025-01-01'
              end_date: '2025-12-31'
              format: json
              framework: OJK_BI_COMBINED
              data_types:
                - policy_violations
                - llm_calls
                - cross_border_transfers
      responses:
        '200':
          description: Export completed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAuditExportResponse'
              example:
                export_id: 3f6d2f4e-8f2a-4a1b-9a51-1de1c1b2c3d4
                status: completed
                framework: OJK_BI_COMBINED
                format: json
                summary:
                  total_records: 15420
                  records_by_type:
                    policy_violations: 142
                    llm_calls: 15278
                  date_range:
                    start: '2025-01-01T00:00:00Z'
                    end: '2025-12-31T00:00:00Z'
                  compliance_score: 92.5
                created_at: '2026-07-10T09:00:00Z'
                metadata:
                  export_version: 1.0.0
                  generated_by: axonflow-ojk-module
                  tenant_id: bank-indonesia-corp
        '400':
          description: |
            Missing tenant headers (code `missing_tenant`), malformed body
            (`invalid_request`), or validation failure (`validation_error`:
            missing/invalid YYYY-MM-DD dates, end before start, range over
            5 years, unsupported format or framework)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
        '500':
          description: Export failed (code `internal_error`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
  /api/v1/ojk/audit/export/{id}:
    get:
      tags:
        - OJK Compliance
      summary: Get OJK export status
      description: |
        Get the status of an OJK audit export by id.

        **Enterprise Feature**.
      operationId: getOJKExportStatus
      parameters:
        - name: id
          in: path
          required: true
          description: Export ID from the export request
          schema:
            type: string
        - name: X-Tenant-ID
          in: header
          required: false
          description: |
            Tenant identifier. The OJK module falls back to X-Org-ID when
            absent; a request carrying neither header is rejected with 400
            (code `missing_tenant`).
          schema:
            type: string
        - name: X-Org-ID
          in: header
          required: false
          description: Fallback tenant identifier when X-Tenant-ID is absent
          schema:
            type: string
      responses:
        '200':
          description: |
            Export status. ⚠️ Current implementation is a **synchronous-echo
            stub**: `GetExportStatus` performs no lookup and returns
            `status: completed` for ANY id (with an empty `format` and a
            fresh `created_at`) — it cannot distinguish a real export from a
            fabricated id (`ojk_audit_export_service.go` GetExportStatus).
            Exports run synchronously, so callers get the data inline from
            the POST and normally never need this endpoint.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAuditExportResponse'
        '400':
          description: >-
            Missing tenant headers (`missing_tenant`) or missing export id
            (`missing_export_id`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
        '404':
          description: |
            Nominally "export not found" (code `not_found`) — in the current
            stub this only fires on an internal error (e.g. database handle
            unavailable), never for an unknown id.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
  /api/v1/ojk/audit/retention:
    get:
      tags:
        - OJK Compliance
      summary: Get OJK retention status
      description: |
        Reports 5-year (1825-day) retention compliance for OJK audit data.
        `compliance_status` is `non_compliant` when the configured retention
        is below the 1825-day minimum.

        **Enterprise Feature**.
      operationId: getOJKRetentionStatus
      parameters:
        - name: X-Tenant-ID
          in: header
          required: false
          description: |
            Tenant identifier. The OJK module falls back to X-Org-ID when
            absent; a request carrying neither header is rejected with 400
            (code `missing_tenant`).
          schema:
            type: string
        - name: X-Org-ID
          in: header
          required: false
          description: Fallback tenant identifier when X-Tenant-ID is absent
          schema:
            type: string
        - name: data_types
          in: query
          required: false
          description: |
            Comma-separated list of data types to report on. ⚠️ Parsed but
            **ignored** by the current implementation — `GetRetentionStatus`
            never reads it and always returns an empty `data_types` array
            (`ojk_audit_export_service.go`).
          schema:
            type: string
          example: policy_violations,llm_calls
      responses:
        '200':
          description: Retention status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKRetentionStatusResponse'
        '400':
          description: Missing tenant headers (code `missing_tenant`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
        '500':
          description: Failed to get retention status (code `internal_error`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
  /api/v1/ojk/audit/readiness:
    get:
      tags:
        - OJK Compliance
      summary: Check OJK compliance readiness
      description: |
        Validates readiness for an OJK regulatory audit across five checks:
        Data Retention, PII Detection, Human Oversight, Audit Logging, and
        Breach Notification. Returns a 0-100 score; `ready` is true at 80+.

        **Enterprise Feature**.
      operationId: getOJKComplianceReadiness
      parameters:
        - name: X-Tenant-ID
          in: header
          required: false
          description: |
            Tenant identifier. The OJK module falls back to X-Org-ID when
            absent; a request carrying neither header is rejected with 400
            (code `missing_tenant`).
          schema:
            type: string
        - name: X-Org-ID
          in: header
          required: false
          description: Fallback tenant identifier when X-Tenant-ID is absent
          schema:
            type: string
      responses:
        '200':
          description: Compliance readiness assessment
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKComplianceReadinessResponse'
              example:
                ready: true
                score: 100
                framework: OJK_BI_COMBINED
                checks:
                  - name: Data Retention
                    description: 5-year (1825-day) retention configured
                    status: pass
                  - name: PII Detection
                    description: Indonesian PII patterns active
                    status: pass
        '400':
          description: Missing tenant headers (code `missing_tenant`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
        '500':
          description: Failed to validate readiness (code `internal_error`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
  /api/v1/ojk/breach/notify:
    post:
      tags:
        - OJK Compliance
      summary: Submit a UU PDP breach notification
      description: |
        Submits a personal-data breach notification per UU PDP Art. 46. The
        server assigns the id, sets `notification_deadline` to
        `discovery_time + 72h`, defaults `notified_authority` to `MOCDA`, and
        stamps `submitted_at`. The returned `status` is normally `submitted`,
        or `overdue` when the 72-hour deadline had already lapsed at
        submission.

        **Enterprise Feature**.
      operationId: notifyOJKBreach
      parameters:
        - name: X-Tenant-ID
          in: header
          required: false
          description: |
            Tenant identifier. The OJK module falls back to X-Org-ID when
            absent; a request carrying neither header is rejected with 400
            (code `missing_tenant`).
          schema:
            type: string
        - name: X-Org-ID
          in: header
          required: false
          description: Fallback tenant identifier when X-Tenant-ID is absent
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OJKBreachNotification'
            example:
              incident_timestamp: '2026-07-08T14:00:00Z'
              discovery_time: '2026-07-09T09:00:00Z'
              data_subjects_affected: 1200
              data_types_involved:
                - nik
                - phone_number
              description: Misconfigured export bucket exposed customer records
              remediation_steps:
                - Bucket policy corrected
                - Access keys rotated
      responses:
        '201':
          description: Breach notification recorded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKBreachNotification'
        '400':
          description: |
            Missing tenant headers (`missing_tenant`), malformed body
            (`invalid_request`), or validation failure (`validation_error`:
            incident_timestamp/discovery_time required, positive
            data_subjects_affected, data_types_involved / description /
            remediation_steps required per UU PDP Art. 46)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
        '500':
          description: Failed to submit breach notification (code `internal_error`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
  /api/v1/ojk/breach/acknowledge:
    post:
      tags:
        - OJK Compliance
      summary: Acknowledge a breach notification
      description: |
        Marks a submitted breach notification as acknowledged by the
        authority. Only the `submitted -> acknowledged` transition is valid;
        acknowledging a draft/overdue/failed/already-acknowledged record
        returns 409.

        **Enterprise Feature**.
      operationId: acknowledgeOJKBreach
      parameters:
        - name: X-Tenant-ID
          in: header
          required: false
          description: |
            Tenant identifier. The OJK module falls back to X-Org-ID when
            absent; a request carrying neither header is rejected with 400
            (code `missing_tenant`).
          schema:
            type: string
        - name: X-Org-ID
          in: header
          required: false
          description: Fallback tenant identifier when X-Tenant-ID is absent
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - id
              properties:
                id:
                  type: string
                  description: Breach notification id
      responses:
        '200':
          description: Breach notification acknowledged (returns the updated record)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKBreachNotification'
        '400':
          description: Missing tenant headers, malformed body, or missing id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
        '404':
          description: Breach notification not found (code `not_found`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
        '409':
          description: >-
            Breach cannot be acknowledged from its current status (code
            `invalid_transition`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
        '500':
          description: Failed to acknowledge breach notification (code `internal_error`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
  /api/v1/ojk/breach/evaluate-deadlines:
    post:
      tags:
        - OJK Compliance
      summary: Sweep lapsed breach-notification deadlines
      description: |
        Flips draft (unsubmitted) breach notifications whose 72-hour UU PDP
        notification deadline has lapsed to `overdue`. Intended to be called
        periodically (deadline sweep).

        **Enterprise Feature**.
      operationId: evaluateOJKBreachDeadlines
      parameters:
        - name: X-Tenant-ID
          in: header
          required: false
          description: |
            Tenant identifier. The OJK module falls back to X-Org-ID when
            absent; a request carrying neither header is rejected with 400
            (code `missing_tenant`).
          schema:
            type: string
        - name: X-Org-ID
          in: header
          required: false
          description: Fallback tenant identifier when X-Tenant-ID is absent
          schema:
            type: string
      responses:
        '200':
          description: Sweep result
          content:
            application/json:
              schema:
                type: object
                properties:
                  flipped_overdue:
                    type: integer
                    description: Number of notifications flipped to overdue
              example:
                flipped_overdue: 2
        '400':
          description: Missing tenant headers (code `missing_tenant`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
        '500':
          description: Failed to evaluate breach deadlines (code `internal_error`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
  /api/v1/ojk/dashboard:
    get:
      tags:
        - OJK Compliance
      summary: Get OJK compliance dashboard
      description: |
        Returns the OJK/UU PDP compliance dashboard: compliance score,
        retention status, breach-notification counts (total and overdue),
        active policies, and recent violations.

        **Enterprise Feature**.
      operationId: getOJKDashboard
      parameters:
        - name: X-Tenant-ID
          in: header
          required: false
          description: |
            Tenant identifier. The OJK module falls back to X-Org-ID when
            absent; a request carrying neither header is rejected with 400
            (code `missing_tenant`).
          schema:
            type: string
        - name: X-Org-ID
          in: header
          required: false
          description: Fallback tenant identifier when X-Tenant-ID is absent
          schema:
            type: string
      responses:
        '200':
          description: OJK compliance dashboard data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKDashboardResponse'
              example:
                framework: OJK_BI_COMBINED
                compliance_score: 100
                total_audit_records: 0
                active_policies: 8
                recent_violations: 0
                retention_status: compliant
                breach_notifications: 3
                overdue_breach_notifications: 0
                last_updated: '2026-07-10T09:00:00Z'
        '400':
          description: Missing tenant headers (code `missing_tenant`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
        '500':
          description: Failed to get dashboard (code `internal_error`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OJKAPIError'
  /api/v1/euaiact/export:
    post:
      tags:
        - EU AI Act
      summary: Create compliance export
      description: >
        Creates a new EU AI Act compliance export job for technical
        documentation (Article 11).


        Export types:

        - `full_audit`: Complete audit trail for regulatory review

        - `conformity_evidence`: Evidence for conformity assessments

        - `hitl_summary`: Human-in-the-loop decision summary

        - `decision_chain`: Full decision chain tracing

        - `policy_violations`: Policy violation records

        - `accuracy_metrics`: Model accuracy and bias metrics
      operationId: createEUAIActExport
      parameters:
        - $ref: '#/components/parameters/OrgIDHeader'
        - $ref: '#/components/parameters/UserIDHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EUAIActExportRequest'
            example:
              export_type: full_audit
              format: json
              date_from: '2025-01-01T00:00:00Z'
              date_to: '2025-12-31T23:59:59Z'
      responses:
        '202':
          description: Export job created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EUAIActExport'
        '400':
          $ref: '#/components/responses/BadRequest'
    get:
      tags:
        - EU AI Act
      summary: List exports
      description: List EU AI Act compliance exports for the organization.
      operationId: listEUAIActExports
      parameters:
        - $ref: '#/components/parameters/OrgIDHeader'
        - $ref: '#/components/parameters/LimitParam'
        - $ref: '#/components/parameters/OffsetParam'
      responses:
        '200':
          description: List of exports
          content:
            application/json:
              schema:
                type: object
                properties:
                  exports:
                    type: array
                    items:
                      $ref: '#/components/schemas/EUAIActExport'
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        '400':
          $ref: '#/components/responses/BadRequest'
  /api/v1/euaiact/export/{export_id}:
    get:
      tags:
        - EU AI Act
      summary: Get export status
      description: Get the status of a specific export job.
      operationId: getEUAIActExport
      parameters:
        - name: export_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Export details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EUAIActExport'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/euaiact/export/{export_id}/download:
    get:
      tags:
        - EU AI Act
      summary: Download export
      description: >
        Download a completed export file. When cloud storage is configured,

        returns a redirect (302) to a presigned URL. For local storage, streams
        the file.
      operationId: downloadEUAIActExport
      parameters:
        - name: export_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Export file metadata or streamed content
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  file_path:
                    type: string
                  file_size:
                    type: integer
                  format:
                    type: string
                  download_url:
                    type: string
                    description: Presigned URL for cloud storage downloads
                  storage_type:
                    type: string
                    enum:
                      - local
                      - s3
                      - gcs
                      - azure
        '307':
          description: Temporary redirect to presigned cloud storage URL
        '400':
          description: Export not yet completed
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/euaiact/conformity:
    post:
      tags:
        - EU AI Act
      summary: Create conformity assessment
      description: |
        Create a new EU AI Act conformity assessment (Article 43).
        Used to document compliance for high-risk AI systems.
      operationId: createConformityAssessment
      parameters:
        - $ref: '#/components/parameters/OrgIDHeader'
        - $ref: '#/components/parameters/UserIDHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateConformityRequest'
            example:
              system_id: ai-system-001
              system_name: Customer Risk Scoring Model
              risk_category: high-risk
              assessors:
                - compliance@company.com
      responses:
        '201':
          description: Assessment created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConformityAssessment'
        '400':
          $ref: '#/components/responses/BadRequest'
    get:
      tags:
        - EU AI Act
      summary: List conformity assessments
      description: List conformity assessments for the organization.
      operationId: listConformityAssessments
      parameters:
        - $ref: '#/components/parameters/OrgIDHeader'
        - name: status
          in: query
          schema:
            type: string
            enum:
              - draft
              - in_progress
              - submitted
              - approved
              - rejected
        - $ref: '#/components/parameters/LimitParam'
        - $ref: '#/components/parameters/OffsetParam'
      responses:
        '200':
          description: List of assessments
          content:
            application/json:
              schema:
                type: object
                properties:
                  assessments:
                    type: array
                    items:
                      $ref: '#/components/schemas/ConformityAssessment'
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
  /api/v1/euaiact/conformity/{assessment_id}:
    get:
      tags:
        - EU AI Act
      summary: Get conformity assessment
      description: Get details of a specific conformity assessment.
      operationId: getConformityAssessment
      parameters:
        - name: assessment_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Assessment details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConformityAssessment'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags:
        - EU AI Act
      summary: Update conformity assessment
      description: >-
        Update a conformity assessment (only allowed for draft/in_progress
        status).
      operationId: updateConformityAssessment
      parameters:
        - name: assessment_id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateConformityRequest'
      responses:
        '200':
          description: Assessment updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConformityAssessment'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/euaiact/conformity/{assessment_id}/submit:
    post:
      tags:
        - EU AI Act
      summary: Submit assessment for review
      description: Submit a conformity assessment for approval review.
      operationId: submitConformityAssessment
      parameters:
        - name: assessment_id
          in: path
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/UserIDHeader'
      responses:
        '200':
          description: Assessment submitted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConformityAssessment'
        '400':
          $ref: '#/components/responses/BadRequest'
  /api/v1/euaiact/conformity/{assessment_id}/approve:
    post:
      tags:
        - EU AI Act
      summary: Approve assessment
      description: Approve a submitted conformity assessment.
      operationId: approveConformityAssessment
      parameters:
        - name: assessment_id
          in: path
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/UserIDHeader'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                validity_years:
                  type: integer
                  default: 1
                  description: Number of years the approval is valid
      responses:
        '200':
          description: Assessment approved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConformityAssessment'
        '400':
          $ref: '#/components/responses/BadRequest'
  /api/v1/euaiact/conformity/{assessment_id}/reject:
    post:
      tags:
        - EU AI Act
      summary: Reject assessment
      description: Reject a submitted conformity assessment.
      operationId: rejectConformityAssessment
      parameters:
        - name: assessment_id
          in: path
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/UserIDHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - reason
              properties:
                reason:
                  type: string
                  description: Reason for rejection
      responses:
        '200':
          description: Assessment rejected
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConformityAssessment'
        '400':
          $ref: '#/components/responses/BadRequest'
  /api/v1/euaiact/accuracy:
    get:
      tags:
        - EU AI Act
      summary: Get accuracy summary
      description: >
        Get accuracy and bias tracking summary for the organization (Article
        15).

        Provides overview of model performance and compliance status.
      operationId: getAccuracySummary
      parameters:
        - $ref: '#/components/parameters/OrgIDHeader'
      responses:
        '200':
          description: Accuracy summary
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccuracySummary'
        '400':
          $ref: '#/components/responses/BadRequest'
  /api/v1/euaiact/accuracy/record:
    post:
      tags:
        - EU AI Act
      summary: Record accuracy metric
      description: Record an accuracy metric for a model.
      operationId: recordAccuracyMetric
      parameters:
        - $ref: '#/components/parameters/OrgIDHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RecordAccuracyRequest'
            example:
              model_id: model-001
              metric_type: accuracy
              value: 0.95
              sample_size: 10000
      responses:
        '201':
          description: Metric recorded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccuracyMetric'
        '400':
          $ref: '#/components/responses/BadRequest'
  /api/v1/euaiact/accuracy/bias:
    post:
      tags:
        - EU AI Act
      summary: Record bias measurement
      description: Record a bias detection measurement for a model.
      operationId: recordBiasMeasurement
      parameters:
        - $ref: '#/components/parameters/OrgIDHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RecordBiasRequest'
            example:
              model_id: model-001
              category: gender
              group_a: male
              group_b: female
              group_a_rate: 0.82
              group_b_rate: 0.79
              sample_size: 5000
      responses:
        '201':
          description: Bias record created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BiasRecord'
        '400':
          $ref: '#/components/responses/BadRequest'
  /api/v1/euaiact/accuracy/history:
    get:
      tags:
        - EU AI Act
      summary: Get accuracy history
      description: Get historical accuracy metrics for filtering and analysis.
      operationId: getAccuracyHistory
      parameters:
        - $ref: '#/components/parameters/OrgIDHeader'
        - name: model_id
          in: query
          schema:
            type: string
        - name: metric_type
          in: query
          schema:
            type: string
            enum:
              - accuracy
              - precision
              - recall
              - f1_score
              - auc_roc
              - auc_pr
              - mse
              - mae
              - custom
        - name: from
          in: query
          schema:
            type: string
            format: date-time
        - name: to
          in: query
          schema:
            type: string
            format: date-time
        - $ref: '#/components/parameters/LimitParam'
        - $ref: '#/components/parameters/OffsetParam'
      responses:
        '200':
          description: Accuracy metrics history
          content:
            application/json:
              schema:
                type: object
                properties:
                  metrics:
                    type: array
                    items:
                      $ref: '#/components/schemas/AccuracyMetric'
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
  /api/v1/euaiact/accuracy/alerts:
    get:
      tags:
        - EU AI Act
      summary: Get active alerts
      description: Get active accuracy and bias alerts for the organization.
      operationId: getAccuracyAlerts
      parameters:
        - $ref: '#/components/parameters/OrgIDHeader'
      responses:
        '200':
          description: Active alerts
          content:
            application/json:
              schema:
                type: object
                properties:
                  alerts:
                    type: array
                    items:
                      $ref: '#/components/schemas/AccuracyAlert'
                  total:
                    type: integer
  /api/v1/euaiact/accuracy/alerts/{alert_id}/acknowledge:
    post:
      tags:
        - EU AI Act
      summary: Acknowledge alert
      description: Acknowledge an accuracy or bias alert.
      operationId: acknowledgeAccuracyAlert
      parameters:
        - name: alert_id
          in: path
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/UserIDHeader'
      responses:
        '200':
          description: Alert acknowledged
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: acknowledged
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/euaiact/accuracy/alerts/{alert_id}/resolve:
    post:
      tags:
        - EU AI Act
      summary: Resolve alert
      description: Resolve an accuracy or bias alert.
      operationId: resolveAccuracyAlert
      parameters:
        - name: alert_id
          in: path
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/UserIDHeader'
      responses:
        '200':
          description: Alert resolved
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: resolved
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/workflows/execute:
    post:
      tags:
        - Workflows
      summary: Execute a workflow
      description: Execute a defined workflow with input parameters
      operationId: executeWorkflow
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkflowExecuteRequest'
            example:
              workflow:
                metadata:
                  name: data-analysis-workflow
                  description: Analyze sales data
                spec:
                  steps:
                    - name: fetch_data
                      type: mcp_query
                      connector: postgres
                      query: SELECT * FROM sales
                    - name: analyze
                      type: llm
                      prompt: 'Analyze the sales data: {{fetch_data.result}}'
              input:
                start_date: '2025-01-01'
                end_date: '2025-01-15'
              user:
                id: 123
                email: analyst@company.com
                role: analyst
                tenant_id: tenant-abc
      responses:
        '200':
          description: Workflow execution started
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowExecution'
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalError'
  /api/v1/workflows/executions/{id}:
    get:
      tags:
        - Workflows
      summary: Get workflow execution
      description: Get details of a specific workflow execution
      operationId: getWorkflowExecution
      parameters:
        - name: id
          in: path
          required: true
          description: Workflow execution ID
          schema:
            type: string
          example: exec_abc123
      responses:
        '200':
          description: Workflow execution details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowExecution'
        '404':
          description: Execution not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/workflows/executions:
    get:
      tags:
        - Workflows
      summary: List workflow executions
      description: List recent workflow executions
      operationId: listWorkflowExecutions
      parameters:
        - name: limit
          in: query
          description: Maximum number of executions to return
          schema:
            type: integer
            default: 10
            minimum: 1
            maximum: 100
      responses:
        '200':
          description: List of executions
          content:
            application/json:
              schema:
                type: object
                properties:
                  executions:
                    type: array
                    items:
                      $ref: '#/components/schemas/WorkflowExecution'
                  count:
                    type: integer
  /api/v1/workflows/executions/tenant/{tenant_id}:
    get:
      tags:
        - Workflows
      summary: Get tenant workflow executions
      description: Get workflow executions for a specific tenant
      operationId: getTenantWorkflowExecutions
      parameters:
        - name: tenant_id
          in: path
          required: true
          description: Tenant identifier
          schema:
            type: string
      responses:
        '200':
          description: Tenant workflow executions
          content:
            application/json:
              schema:
                type: object
                properties:
                  tenant_id:
                    type: string
                  count:
                    type: integer
                  executions:
                    type: array
                    items:
                      $ref: '#/components/schemas/WorkflowExecution'
  /api/v1/workflows:
    post:
      tags:
        - Workflow Control Plane
      summary: Create a workflow
      description: >
        Register a new workflow from an external orchestrator (LangChain,
        LangGraph, CrewAI).

        Returns a workflow_id to use for subsequent step gate checks.
      operationId: createControlPlaneWorkflow
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWorkflowRequest'
            example:
              workflow_name: code-review-pipeline
              source: langgraph
              metadata:
                environment: production
                team: engineering
      responses:
        '201':
          description: Workflow created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateWorkflowResponse'
              example:
                workflow_id: wf_abc123
                workflow_name: code-review-pipeline
                status: in_progress
                started_at: '2026-01-17T10:00:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
    get:
      tags:
        - Workflow Control Plane
      summary: List workflows
      description: List workflows with optional filters
      operationId: listControlPlaneWorkflows
      parameters:
        - name: status
          in: query
          description: Filter by status
          schema:
            type: string
            enum:
              - in_progress
              - completed
              - aborted
              - failed
        - name: source
          in: query
          description: Filter by source
          schema:
            type: string
            enum:
              - langgraph
              - langchain
              - crewai
              - external
        - name: limit
          in: query
          description: Maximum number of workflows to return
          schema:
            type: integer
            default: 50
            minimum: 1
            maximum: 100
        - name: offset
          in: query
          description: Number of workflows to skip
          schema:
            type: integer
            default: 0
        - name: trace_id
          in: query
          description: Filter by external trace ID
          schema:
            type: string
      responses:
        '200':
          description: List of workflows
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListWorkflowsResponse'
  /api/v1/workflows/{workflow_id}:
    get:
      tags:
        - Workflow Control Plane
      summary: Get workflow status
      description: Get the current status of a workflow including all step decisions
      operationId: getControlPlaneWorkflow
      parameters:
        - name: workflow_id
          in: path
          required: true
          description: Workflow ID
          schema:
            type: string
          example: wf_abc123
      responses:
        '200':
          description: Workflow status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowStatusResponse'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/workflows/{workflow_id}/steps/{step_id}/gate:
    post:
      tags:
        - Workflow Control Plane
      summary: Check step gate
      description: >
        Check if a workflow step is allowed to proceed.

        Returns a decision (allow/block/require_approval) based on policy
        evaluation.


        Call this BEFORE executing each step in your external orchestrator.


        The response always includes a `retry_context` block (Issue #1673 Phase
        1)

        carrying first-class retry state: gate count, prior completion status,

        first/last attempt timestamps, last decision, and the idempotency_key.

        Callers that need to unambiguously detect retries or uncertain-territory

        scenarios should prefer `retry_context` over the deprecated `cached`

        boolean.
      operationId: checkStepGate
      parameters:
        - name: workflow_id
          in: path
          required: true
          description: Workflow ID
          schema:
            type: string
          example: wf_abc123
        - name: step_id
          in: path
          required: true
          description: Step ID (unique within workflow)
          schema:
            type: string
          example: step-1
        - name: include_prior_output
          in: query
          required: false
          description: |
            Opt-in (Issue #1673 Phase 1) — when `true` and a prior /complete
            landed for this step, `retry_context.prior_output` is populated
            with the stored output so the agent can short-circuit re-execution.
            Default `false` because prior output may be large or sensitive.
          schema:
            type: boolean
            default: false
          example: true
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StepGateRequest'
            example:
              step_name: Generate Code
              step_type: llm_call
              model: gpt-4
              provider: openai
              step_input:
                prompt: Write a function to sort a list
      responses:
        '200':
          description: Gate decision
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StepGateResponse'
              examples:
                allowed:
                  summary: Step allowed
                  value:
                    decision: allow
                    step_id: step-1
                    decision_id: dec_xyz789
                blocked:
                  summary: Step blocked
                  value:
                    decision: block
                    step_id: step-1
                    reason: GPT-4 not allowed in production workflows
                    policy_ids:
                      - policy_gpt4_block
                approval_required:
                  summary: Approval required
                  value:
                    decision: require_approval
                    step_id: step-1
                    reason: Human approval required for deployment steps
                    approval_url: https://portal.axonflow.com/approvals/abc123
        '400':
          description: |
            Bad request — missing step_type, invalid retry_policy, or
            idempotency_key exceeds 255 characters.
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: |
            `IDEMPOTENCY_KEY_MISMATCH` (Issue #1673 Phase 2) — the supplied
            `idempotency_key` does not match the one recorded on the step's
            earlier /gate call. `expected_idempotency_key` and
            `received_idempotency_key` are always present, empty string when
            one side is absent.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIErrorResponse'
              example:
                error:
                  code: IDEMPOTENCY_KEY_MISMATCH
                  message: idempotency_key does not match the key recorded on gate
                  details:
                    workflow_id: wf_41231a72
                    step_id: step-1
                    expected_idempotency_key: payment:wire:invoice-7721
                    received_idempotency_key: payment:wire:invoice-9999
  /api/v1/workflows/{workflow_id}/steps/{step_id}/complete:
    post:
      tags:
        - Workflow Control Plane
      summary: Mark step completed
      description: >-
        Mark a workflow step as completed after successful execution. Request
        body is optional.
      operationId: markStepCompleted
      parameters:
        - name: workflow_id
          in: path
          required: true
          description: Workflow ID
          schema:
            type: string
        - name: step_id
          in: path
          required: true
          description: Step ID
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MarkStepCompletedRequest'
            example:
              output:
                code: 'def sort_list(items): return sorted(items)'
              tokens_in: 150
              tokens_out: 45
              cost_usd: 0.0023
      responses:
        '204':
          description: Step marked completed
        '400':
          description: idempotency_key exceeds 255 characters
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: |
            `IDEMPOTENCY_KEY_MISMATCH` (Issue #1673 Phase 2) — the supplied
            `idempotency_key` does not match the one recorded on the step's
            earlier /gate call.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APIErrorResponse'
  /api/v1/workflows/{workflow_id}/complete:
    post:
      tags:
        - Workflow Control Plane
      summary: Complete workflow
      description: Mark the workflow as completed
      operationId: completeControlPlaneWorkflow
      parameters:
        - name: workflow_id
          in: path
          required: true
          description: Workflow ID
          schema:
            type: string
      responses:
        '200':
          description: Workflow completed
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/workflows/{workflow_id}/fail:
    post:
      tags:
        - Workflow Control Plane
      summary: Mark workflow as failed
      description: |
        Marks the workflow as failed with an optional reason (defaults to
        `Failed` when the body is empty or omitted). Tenant/org scoping comes
        from the `X-Tenant-ID` / `X-Org-ID` headers. Failing a workflow that
        is already in a terminal state returns 409.
      operationId: failControlPlaneWorkflow
      parameters:
        - name: workflow_id
          in: path
          required: true
          description: Workflow ID
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                reason:
                  type: string
                  description: Reason for the failure (defaults to "Failed")
            example:
              reason: Downstream connector unrecoverable
      responses:
        '200':
          description: Workflow marked as failed
          content:
            application/json:
              schema:
                type: object
                properties:
                  workflow_id:
                    type: string
                  status:
                    type: string
                    example: failed
                  message:
                    type: string
                    example: Workflow marked as failed
                  reason:
                    type: string
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Workflow is already in a terminal state (code WORKFLOW_TERMINAL)
  /api/v1/workflows/{workflow_id}/abort:
    post:
      tags:
        - Workflow Control Plane
      summary: Abort workflow
      description: Abort the workflow with an optional reason
      operationId: abortControlPlaneWorkflow
      parameters:
        - name: workflow_id
          in: path
          required: true
          description: Workflow ID
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                reason:
                  type: string
                  description: Reason for aborting
            example:
              reason: Step blocked by policy
      responses:
        '200':
          description: Workflow aborted
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/workflows/{workflow_id}/resume:
    post:
      tags:
        - Workflow Control Plane
      summary: Resume workflow
      description: Resume a workflow after approval (Enterprise feature)
      operationId: resumeControlPlaneWorkflow
      parameters:
        - name: workflow_id
          in: path
          required: true
          description: Workflow ID
          schema:
            type: string
      responses:
        '200':
          description: Workflow resumed
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/workflows/{workflow_id}/checkpoints:
    get:
      tags:
        - Workflow Control Plane
      summary: List step-gate checkpoints for a workflow
      description: |
        Returns all checkpoints for a workflow, ordered by step_index.
        Checkpoints are created automatically at each step gate evaluation.
        Available in all tiers (Community, Evaluation, Enterprise).
      operationId: getCheckpoints
      parameters:
        - name: workflow_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: List of checkpoints
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CheckpointListResponse'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/workflows/{workflow_id}/checkpoints/resume:
    post:
      tags:
        - Workflow Control Plane
      summary: Resume workflow from last checkpoint (Evaluation+)
      description: |
        Re-evaluates the step gate at the last resumable checkpoint with
        current policies. The step gate uses retry_policy=reevaluate internally.
      operationId: resumeFromLastCheckpoint
      parameters:
        - name: workflow_id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Resume result with fresh decision
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResumeFromCheckpointResponse'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Workflow not resumable (completed, no checkpoints)
  /api/v1/workflows/{workflow_id}/checkpoints/{checkpoint_id}/resume:
    post:
      tags:
        - Workflow Control Plane
      summary: Resume workflow from specific checkpoint (Enterprise)
      description: >
        Re-evaluates the step gate at a specific checkpoint with current
        policies.

        Enterprise only.
      operationId: resumeFromCheckpoint
      parameters:
        - name: workflow_id
          in: path
          required: true
          schema:
            type: string
        - name: checkpoint_id
          in: path
          required: true
          schema:
            type: integer
            format: int64
      responses:
        '200':
          description: Resume result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResumeFromCheckpointResponse'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Checkpoint not resumable
  /api/v1/workflows/{workflowId}/steps/{stepId}/approve:
    post:
      tags:
        - Workflow Control Plane
      summary: Approve a pending workflow step
      description: |
        Approve a workflow step that is waiting for human approval.
        This allows the workflow to proceed past the approval gate.
      operationId: approveWorkflowStep
      parameters:
        - name: workflowId
          in: path
          required: true
          description: Workflow ID
          schema:
            type: string
          example: wf_abc123
        - name: stepId
          in: path
          required: true
          description: Step ID awaiting approval
          schema:
            type: string
          example: step-2
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - comment
              properties:
                comment:
                  type: string
                  minLength: 10
                  description: >-
                    Audit justification for approving the step (minimum 10
                    characters after trimming)
                approved_by:
                  type: string
                  description: User ID of the approver
            example:
              comment: Approved after reviewing output
              approved_by: user-456
      responses:
        '200':
          description: Step approved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalResponse'
              example:
                workflow_id: wf_abc123
                step_id: step-2
                status: approved
                decision: allow
                reason: 'Approved: High-value transfer requires oversight'
                approval_status: approved
                approval_id: 318a270f-7b42-5c56-a191-8dbd1bf2e1e4
                approved_by: fraud.analyst@banking.example
                approved_at: '2026-04-22T10:05:00Z'
                policies_matched:
                  - policy_id: high-value-wire-oversight
                    policy_name: High-Value Wire Transfer Oversight
                    action: require_approval
                retry_context:
                  gate_count: 1
                  completion_count: 0
                  prior_completion_status: none
                  prior_output_available: false
                  prior_output: null
                  prior_completion_at: null
                  idempotency_key: payment-intent-123
                  last_decision: require_approval
                  first_attempt_at: '2026-04-22T10:00:00Z'
                  last_attempt_at: '2026-04-22T10:00:00Z'
                message: Step approved
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Step is not in a pending approval state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error: Step step-2 is not awaiting approval
  /api/v1/workflows/{workflowId}/steps/{stepId}/reject:
    post:
      tags:
        - Workflow Control Plane
      summary: Reject a pending workflow step
      description: >
        Reject a workflow step that is waiting for human approval.

        This blocks the step and may abort the workflow depending on
        configuration.
      operationId: rejectWorkflowStep
      parameters:
        - name: workflowId
          in: path
          required: true
          description: Workflow ID
          schema:
            type: string
          example: wf_abc123
        - name: stepId
          in: path
          required: true
          description: Step ID awaiting approval
          schema:
            type: string
          example: step-2
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - reason
              properties:
                reason:
                  type: string
                  minLength: 10
                  description: >-
                    Audit justification for rejecting the step (minimum 10
                    characters after trimming)
                rejected_by:
                  type: string
                  description: User ID of the rejector
            example:
              reason: Output contains PII that was not redacted
              rejected_by: user-456
      responses:
        '200':
          description: Step rejected successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalResponse'
              example:
                workflow_id: wf_abc123
                step_id: step-2
                status: rejected
                decision: block
                reason: 'Rejected: Output contains PII that was not redacted'
                approval_status: rejected
                approval_id: 318a270f-7b42-5c56-a191-8dbd1bf2e1e4
                rejected_by: fraud.analyst@banking.example
                rejected_at: '2026-04-22T10:05:00Z'
                policies_matched:
                  - policy_id: pii-output-redaction
                    policy_name: PII Redaction Required
                    action: require_approval
                retry_context:
                  gate_count: 1
                  completion_count: 0
                  prior_completion_status: none
                  prior_output_available: false
                  prior_output: null
                  prior_completion_at: null
                  idempotency_key: ''
                  last_decision: require_approval
                  first_attempt_at: '2026-04-22T10:00:00Z'
                  last_attempt_at: '2026-04-22T10:00:00Z'
                message: Step rejected, workflow aborted
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Step is not in a pending approval state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error: Step step-2 is not awaiting approval
  /api/v1/plans/{planId}/steps/{stepId}/approve:
    post:
      tags:
        - Multi-Agent Planning
      summary: Approve a MAP plan step (HITL parity with WCP)
      description: >
        Plan-scoped approval endpoint. Returns the same `ApprovalResponse`

        shape as the WCP endpoint
        (`/api/v1/workflows/{id}/steps/{step_id}/approve`)

        plus a `plan_id` field — see ADR-046 (HITL response parity).


        Two underlying flows, identical response:

        - MAP **confirm** / **step** modes — backed by a WCP workflow; the
          handler delegates to the WCP service and projects the full
          `retry_context`, approver metadata, and `policies_matched`.
        - MAP **legacy** in-memory flow (policy-driven pause/resume, no WCP
          workflow registered) — handler falls back to the in-memory
          execution store and projects a minimal response with
          `retry_context` zero-valued and `approval_id` from the execution
          record.
      operationId: approveMAPPlanStep
      parameters:
        - name: planId
          in: path
          required: true
          description: MAP plan ID
          schema:
            type: string
          example: plan-abc123
        - name: stepId
          in: path
          required: true
          description: Step ID awaiting approval
          schema:
            type: string
          example: step_0_analyze
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                approved_by:
                  type: string
                  description: Identity approving the step (overrides X-User-ID header)
                comment:
                  type: string
                  description: >-
                    Audit justification. Required on WCP-backed plans (min 10
                    chars); if shorter, the handler auto-fills a generated audit
                    message.
            example:
              approved_by: fraud.analyst@banking.example
              comment: Approved after full audit review of the payment intent
      responses:
        '200':
          description: Step approved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalResponse'
              example:
                workflow_id: wf_abc123
                plan_id: plan-abc123
                step_id: step_0_analyze
                status: approved
                decision: allow
                reason: 'Approved: High-value transfer requires oversight'
                approval_status: approved
                approval_id: 318a270f-7b42-5c56-a191-8dbd1bf2e1e4
                approved_by: fraud.analyst@banking.example
                approved_at: '2026-04-22T10:05:00Z'
                policies_matched:
                  - policy_id: high-value-wire-oversight
                    policy_name: High-Value Wire Transfer Oversight
                    action: require_approval
                retry_context:
                  gate_count: 1
                  completion_count: 0
                  prior_completion_status: none
                  prior_output_available: false
                  prior_output: null
                  prior_completion_at: null
                  idempotency_key: payment-intent-123
                  last_decision: require_approval
                  first_attempt_at: '2026-04-22T10:00:00Z'
                  last_attempt_at: '2026-04-22T10:00:00Z'
                message: Step approved
        '403':
          description: MAP step approval requires Enterprise license (community mode)
        '404':
          description: No paused execution / plan step found
        '503':
          description: HITL is not enabled in the orchestrator configuration
  /api/v1/plans/{planId}/steps/{stepId}/reject:
    post:
      tags:
        - Multi-Agent Planning
      summary: Reject a MAP plan step (HITL parity with WCP)
      description: |
        Plan-scoped rejection endpoint. Symmetric with the WCP endpoint
        (`/api/v1/workflows/{id}/steps/{step_id}/reject`) — same response
        shape (`ApprovalResponse`) plus `plan_id`. See ADR-046.

        Rejection aborts the workflow / plan. On WCP-backed plans the WCP
        service's `RejectStep` handles the abort; on the legacy in-memory
        flow, the HITL workflow engine's `AbortExecution` handles it.
      operationId: rejectMAPPlanStep
      parameters:
        - name: planId
          in: path
          required: true
          schema:
            type: string
        - name: stepId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                rejected_by:
                  type: string
                reason:
                  type: string
                  description: >-
                    Audit justification. Required on WCP-backed plans (min 10
                    chars); auto-filled otherwise.
            example:
              rejected_by: fraud.analyst@banking.example
              reason: Output contains PII that was not redacted
      responses:
        '200':
          description: Step rejected successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalResponse'
        '403':
          description: MAP step rejection requires Enterprise license
        '404':
          description: No paused execution / plan step found
        '503':
          description: HITL is not enabled
  /api/v1/workflows/approvals/pending:
    get:
      tags:
        - Workflow Control Plane
      summary: List pending approvals (WCP plane)
      description: |
        List workflow steps currently awaiting human approval for the caller's
        tenant (all planes). Cross-reference the MAP-plane equivalent at
        `/api/v1/plans/approvals/pending`, which scopes to MAP-backed
        workflows and populates `plan_id` on every entry.

        Available on Evaluation+ licenses. Community without an Evaluation
        license has no approval queue to list.
      operationId: listPendingApprovals
      parameters:
        - name: limit
          in: query
          description: Maximum number of results to return
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
      responses:
        '200':
          description: List of pending approvals
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PendingApprovalsResponse'
              example:
                pending_approvals:
                  - workflow_id: wf_abc123
                    workflow_name: code-review-pipeline
                    step_id: step-2
                    step_index: 1
                    step_name: Deploy to Production
                    step_type: action
                    decision: require_approval
                    decision_reason: Human approval required for deployment steps
                    approval_status: pending
                    created_at: '2026-04-22T10:00:00Z'
                count: 1
        '400':
          description: Tenant ID missing from request
        '403':
          description: Tier does not permit approval listing
  /api/v1/plans/approvals/pending:
    get:
      tags:
        - MAP
        - Workflow Control Plane
      summary: List pending approvals (MAP plane)
      description: |
        List steps currently awaiting human approval for MAP-backed workflows —
        workflows whose metadata carries a `plan_id` (MAP confirm / step mode).
        Every returned entry has `plan_id` populated; this is the intentional
        asymmetry with the WCP-plane listing at
        `/api/v1/workflows/approvals/pending`, mirroring the approve/reject
        asymmetry established in Issue #1677 / ADR-046.

        Reviewer integrators that need to render plan context can read
        `plan_id` directly without a second lookup; clients that want a
        plane-neutral view can use `/api/v1/hitl/queue` instead.

        Available on Evaluation+ licenses (same tier gate as the MAP
        `/steps/{step_id}/approve` and `/steps/{step_id}/reject` endpoints).
      operationId: listPendingPlanApprovals
      parameters:
        - name: plan_id
          in: query
          description: >-
            Filter to a single plan_id — returns only steps waiting on that
            plan.
          required: false
          schema:
            type: string
        - name: limit
          in: query
          description: Maximum number of results to return
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 100
      responses:
        '200':
          description: List of pending plan approvals
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PendingApprovalsResponse'
              example:
                pending_approvals:
                  - workflow_id: wf_map_abc
                    workflow_name: map-confirm-plan-abc123
                    plan_id: plan-abc123
                    step_id: step_0_analyze
                    step_index: 0
                    step_name: Analyze customer transaction
                    step_type: tool_call
                    decision: require_approval
                    decision_reason: High-value transaction requires review
                    approval_status: pending
                    created_at: '2026-04-22T10:00:00Z'
                count: 1
        '400':
          description: Tenant ID missing from request
        '403':
          description: >-
            License tier does not permit approval listing (community without
            Evaluation license)
        '503':
          description: Workflow control plane unavailable
  /api/v1/connectors:
    get:
      tags:
        - Connectors
      summary: List available connectors
      description: List connectors from the marketplace
      operationId: listConnectors
      responses:
        '200':
          description: List of connectors
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ConnectorInfo'
  /api/v1/connectors/{id}:
    get:
      tags:
        - Connectors
      summary: Get connector details
      description: Get detailed information about a connector
      operationId: getConnectorDetails
      parameters:
        - name: id
          in: path
          required: true
          description: Connector identifier
          schema:
            type: string
      responses:
        '200':
          description: Connector details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConnectorInfo'
        '404':
          description: Connector not found
  /api/v1/connectors/{id}/install:
    post:
      tags:
        - Connectors
      summary: Install a connector
      description: Install a connector from the marketplace
      operationId: installConnector
      parameters:
        - name: id
          in: path
          required: true
          description: Connector identifier
          schema:
            type: string
      responses:
        '200':
          description: Connector installed
        '400':
          $ref: '#/components/responses/BadRequest'
  /api/v1/connectors/{id}/uninstall:
    delete:
      tags:
        - Connectors
      summary: Uninstall a connector
      description: Uninstall an installed connector
      operationId: uninstallConnector
      parameters:
        - name: id
          in: path
          required: true
          description: Connector identifier
          schema:
            type: string
      responses:
        '200':
          description: Connector uninstalled
        '404':
          description: Connector not found
  /api/v1/connectors/{id}/health:
    get:
      tags:
        - Connectors
      summary: Check connector health
      description: Check health status of a connector
      operationId: getConnectorHealth
      parameters:
        - name: id
          in: path
          required: true
          description: Connector identifier
          schema:
            type: string
      responses:
        '200':
          description: Connector health status
          content:
            application/json:
              schema:
                type: object
                properties:
                  healthy:
                    type: boolean
                  latency_ms:
                    type: integer
        '404':
          description: Connector not found
  /api/v1/rbi/dashboard:
    get:
      tags:
        - RBI Compliance
      summary: Get RBI compliance dashboard
      description: >
        Returns RBI FREE-AI Framework compliance dashboard with module health
        status.
      operationId: getRBIDashboard
      responses:
        '200':
          description: Dashboard data
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  module:
                    type: string
                  components:
                    type: object
  /api/v1/rbi/ai-systems:
    get:
      tags:
        - RBI Compliance
      summary: List AI systems
      description: |
        List all registered AI systems for the organization.
        Per RBI FREE-AI: All AI systems must be registered with board approval.
      operationId: listAISystems
      parameters:
        - name: risk_category
          in: query
          schema:
            type: string
            enum:
              - low
              - medium
              - high
        - name: deployment_status
          in: query
          schema:
            type: string
            enum:
              - development
              - sandbox
              - canary
              - production
              - deprecated
      responses:
        '200':
          description: List of AI systems
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/RBIAISystem'
    post:
      tags:
        - RBI Compliance
      summary: Register AI system
      description: Register a new AI system in the RBI compliance registry.
      operationId: createAISystem
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RBIAISystemCreate'
      responses:
        '201':
          description: AI system created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RBIAISystem'
  /api/v1/rbi/ai-systems/{id}:
    get:
      tags:
        - RBI Compliance
      summary: Get AI system
      operationId: getAISystem
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: AI system details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RBIAISystem'
    put:
      tags:
        - RBI Compliance
      summary: Update AI system
      operationId: updateAISystem
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RBIAISystemCreate'
      responses:
        '200':
          description: AI system updated
    delete:
      tags:
        - RBI Compliance
      summary: Delete AI system
      operationId: deleteAISystem
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '204':
          description: AI system deleted
  /api/v1/rbi/validations:
    get:
      tags:
        - RBI Compliance
      summary: List model validations
      description: List model validation records per RBI FREE-AI Section 3.2.
      operationId: listValidations
      responses:
        '200':
          description: List of validations
    post:
      tags:
        - RBI Compliance
      summary: Create validation record
      operationId: createValidation
      responses:
        '201':
          description: Validation created
  /api/v1/rbi/incidents:
    get:
      tags:
        - RBI Compliance
      summary: List AI incidents
      description: List AI incidents per RBI FREE-AI incident management requirements.
      operationId: listIncidents
      responses:
        '200':
          description: List of incidents
    post:
      tags:
        - RBI Compliance
      summary: Report AI incident
      operationId: createIncident
      responses:
        '201':
          description: Incident created
  /api/v1/rbi/killswitches:
    get:
      tags:
        - RBI Compliance
      summary: List kill switches
      description: List active and inactive kill switches for emergency AI disable.
      operationId: listKillSwitches
      responses:
        '200':
          description: List of kill switches
    post:
      tags:
        - RBI Compliance
      summary: Activate kill switch
      description: |
        Emergency kill switch activation per RBI FREE-AI guidelines.
        Immediately halts all AI operations for specified scope.
      operationId: activateKillSwitch
      responses:
        '201':
          description: Kill switch activated
  /api/v1/rbi/killswitches/{id}/deactivate:
    post:
      tags:
        - RBI Compliance
      summary: Deactivate kill switch
      operationId: deactivateKillSwitch
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Kill switch deactivated
  /api/v1/rbi/reports:
    get:
      tags:
        - RBI Compliance
      summary: List board reports
      description: List board reports per RBI FREE-AI Section 6.1 requirements.
      operationId: listBoardReports
      responses:
        '200':
          description: List of reports
    post:
      tags:
        - RBI Compliance
      summary: Generate board report
      operationId: createBoardReport
      responses:
        '201':
          description: Report generation started
  /api/v1/rbi/audit-exports:
    get:
      tags:
        - RBI Compliance
      summary: List audit exports
      description: List audit exports with retention per RBI FREE-AI requirements.
      operationId: listAuditExports
      parameters:
        - name: X-Org-ID
          in: header
          required: true
          description: |
            Organization scope for this request. Stamped by the AxonFlow Agent
            gateway from the cryptographically validated client credential
            (Set, not Add, so any client-supplied value is overwritten), so it
            is not client-selectable and carries no cross-org override
            capability on this route. The orchestrator fails closed with 401
            when it is absent or blank; there is no query-string equivalent.
          schema:
            type: string
          example: travel-us
        - $ref: '#/components/parameters/LimitParam'
        - $ref: '#/components/parameters/OffsetParam'
      responses:
        '200':
          description: List of exports
          content:
            application/json:
              schema:
                type: object
                properties:
                  exports:
                    type: array
                    items:
                      $ref: '#/components/schemas/RBIAuditExport'
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags:
        - RBI Compliance
      summary: Create audit export
      description: >
        Create a new RBI audit export. Supports full or incremental exports in
        JSON, CSV, or XML format.

        When cloud storage is configured, exports are uploaded to S3/GCS/Azure
        and a presigned download URL is generated.
      operationId: createAuditExport
      parameters:
        - name: X-Org-ID
          in: header
          required: true
          description: |
            Organization scope for this request. Stamped by the AxonFlow Agent
            gateway from the cryptographically validated client credential
            (Set, not Add, so any client-supplied value is overwritten), so it
            is not client-selectable and carries no cross-org override
            capability on this route. The orchestrator fails closed with 401
            when it is absent or blank; there is no query-string equivalent.
          schema:
            type: string
          example: travel-us
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RBIAuditExportRequest'
            example:
              export_type: full
              format: json
              requested_by: compliance-officer
              purpose: Quarterly RBI audit
      responses:
        '201':
          description: Export created
          content:
            application/json:
              schema:
                type: object
                properties:
                  export:
                    $ref: '#/components/schemas/RBIAuditExport'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /api/v1/rbi/audit-exports/{export_id}:
    get:
      tags:
        - RBI Compliance
      summary: Get audit export status
      description: Get the status and details of a specific audit export.
      operationId: getAuditExport
      parameters:
        - name: export_id
          in: path
          required: true
          schema:
            type: string
        - name: X-Org-ID
          in: header
          required: true
          description: |
            Organization scope for this request. Stamped by the AxonFlow Agent
            gateway from the cryptographically validated client credential
            (Set, not Add, so any client-supplied value is overwritten), so it
            is not client-selectable and carries no cross-org override
            capability on this route. The orchestrator fails closed with 401
            when it is absent or blank; there is no query-string equivalent.
          schema:
            type: string
          example: travel-us
      responses:
        '200':
          description: Export details
          content:
            application/json:
              schema:
                type: object
                properties:
                  export:
                    $ref: '#/components/schemas/RBIAuditExport'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
        - RBI Compliance
      summary: Delete audit export
      description: Delete an audit export and its associated cloud storage object.
      operationId: deleteAuditExport
      parameters:
        - name: export_id
          in: path
          required: true
          schema:
            type: string
        - name: X-Org-ID
          in: header
          required: true
          description: |
            Organization scope for this request. Stamped by the AxonFlow Agent
            gateway from the cryptographically validated client credential
            (Set, not Add, so any client-supplied value is overwritten), so it
            is not client-selectable and carries no cross-org override
            capability on this route. The orchestrator fails closed with 401
            when it is absent or blank; there is no query-string equivalent.
          schema:
            type: string
          example: travel-us
      responses:
        '204':
          description: Export deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/rbi/audit-exports/{export_id}/process:
    post:
      tags:
        - RBI Compliance
      summary: Process audit export
      description: >
        Trigger processing of a pending audit export. Generates the export file,

        uploads to cloud storage (if configured), and generates a presigned
        download URL.
      operationId: processAuditExport
      parameters:
        - name: export_id
          in: path
          required: true
          schema:
            type: string
        - name: X-Org-ID
          in: header
          required: true
          description: |
            Organization scope for this request. Stamped by the AxonFlow Agent
            gateway from the cryptographically validated client credential
            (Set, not Add, so any client-supplied value is overwritten), so it
            is not client-selectable and carries no cross-org override
            capability on this route. The orchestrator fails closed with 401
            when it is absent or blank; there is no query-string equivalent.
          schema:
            type: string
          example: travel-us
      responses:
        '200':
          description: Export processed
          content:
            application/json:
              schema:
                type: object
                properties:
                  export:
                    $ref: '#/components/schemas/RBIAuditExport'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/rbi/policies/templates:
    get:
      tags:
        - RBI Compliance
      summary: List RBI policy templates
      description: List pre-built RBI FREE-AI compliance policy templates.
      operationId: listRBIPolicyTemplates
      responses:
        '200':
          description: List of policy templates
  /api/v1/executions:
    get:
      tags:
        - Decision & Execution Replay
      summary: List workflow executions
      description: |
        List all workflow executions with optional filtering and pagination.
        Supports filtering by status, workflow, tenant, time range.
      operationId: listExecutions
      parameters:
        - name: limit
          in: query
          description: Maximum number of results (default 50)
          schema:
            type: integer
            default: 50
        - name: offset
          in: query
          description: Pagination offset (default 0)
          schema:
            type: integer
            default: 0
        - name: status
          in: query
          description: Filter by execution status
          schema:
            type: string
            enum:
              - pending
              - running
              - completed
              - failed
        - name: workflow_id
          in: query
          description: Filter by workflow name
          schema:
            type: string
        - name: start_time
          in: query
          description: Filter by start time (RFC3339 format)
          schema:
            type: string
            format: date-time
        - name: end_time
          in: query
          description: Filter by end time (RFC3339 format)
          schema:
            type: string
            format: date-time
        - name: X-Tenant-ID
          in: header
          description: Filter by tenant ID
          schema:
            type: string
        - name: X-Org-ID
          in: header
          description: Filter by organization ID
          schema:
            type: string
      responses:
        '200':
          description: List of executions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExecutionListResponse'
  /api/v1/executions/{id}:
    get:
      tags:
        - Decision & Execution Replay
      summary: Get execution details
      description: Get full execution details including summary and all steps.
      operationId: getExecution
      parameters:
        - name: id
          in: path
          required: true
          description: Execution request ID
          schema:
            type: string
      responses:
        '200':
          description: Execution details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Execution'
        '404':
          description: Execution not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    delete:
      tags:
        - Decision & Execution Replay
      summary: Delete execution
      description: Delete an execution and all its step data.
      operationId: deleteExecution
      parameters:
        - name: id
          in: path
          required: true
          description: Execution request ID
          schema:
            type: string
      responses:
        '204':
          description: Execution deleted
        '404':
          description: Execution not found
  /api/v1/executions/{id}/steps:
    get:
      tags:
        - Decision & Execution Replay
      summary: Get execution steps
      description: Get all steps for an execution.
      operationId: getExecutionSteps
      parameters:
        - name: id
          in: path
          required: true
          description: Execution request ID
          schema:
            type: string
      responses:
        '200':
          description: List of execution steps
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ExecutionSnapshot'
        '404':
          description: Execution not found
  /api/v1/executions/{id}/steps/{stepIndex}:
    get:
      tags:
        - Decision & Execution Replay
      summary: Get specific step
      description: Get a specific step by index.
      operationId: getExecutionStep
      parameters:
        - name: id
          in: path
          required: true
          description: Execution request ID
          schema:
            type: string
        - name: stepIndex
          in: path
          required: true
          description: Step index (0-based)
          schema:
            type: integer
      responses:
        '200':
          description: Step details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExecutionSnapshot'
        '404':
          description: Step not found
  /api/v1/executions/{id}/timeline:
    get:
      tags:
        - Decision & Execution Replay
      summary: Get execution timeline
      description: Get a timeline view of execution steps with status indicators.
      operationId: getExecutionTimeline
      parameters:
        - name: id
          in: path
          required: true
          description: Execution request ID
          schema:
            type: string
      responses:
        '200':
          description: Execution timeline
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TimelineEntry'
        '404':
          description: Execution not found
  /api/v1/executions/{id}/export:
    get:
      tags:
        - Decision & Execution Replay
      summary: Export execution
      description: |
        Export full execution record for compliance and auditing.
        Returns a downloadable JSON file.
      operationId: exportExecution
      parameters:
        - name: id
          in: path
          required: true
          description: Execution request ID
          schema:
            type: string
        - name: format
          in: query
          description: Export format (default json)
          schema:
            type: string
            default: json
        - name: include_input
          in: query
          description: Include step inputs (default true)
          schema:
            type: boolean
            default: true
        - name: include_output
          in: query
          description: Include step outputs (default true)
          schema:
            type: boolean
            default: true
        - name: include_policies
          in: query
          description: Include policy events (default true)
          schema:
            type: boolean
            default: true
      responses:
        '200':
          description: Execution export
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExecutionExport'
        '404':
          description: Execution not found
  /api/v1/budgets:
    post:
      tags:
        - Cost Controls
      summary: Create a budget
      description: |
        Create a new budget with spending limits. Budgets can be scoped to
        organization, team, agent, workflow, or user level.

        Enterprise only. These routes are not registered in Community
        edition, which returns 404 Not Found.
      operationId: createBudget
      parameters:
        - name: X-Org-ID
          in: header
          required: true
          description: Organization ID
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BudgetCreate'
            example:
              id: monthly-budget
              name: Monthly Production Budget
              scope: organization
              limit_usd: 1000
              period: monthly
              on_exceed: warn
              alert_thresholds:
                - 50
                - 80
                - 100
      responses:
        '201':
          description: Budget created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Budget'
        '400':
          description: Invalid budget configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Budget with this ID already exists
    get:
      tags:
        - Cost Controls
      summary: List budgets
      description: |
        List all budgets for the organization.

        Enterprise only. These routes are not registered in Community
        edition, which returns 404 Not Found.
      operationId: listBudgets
      parameters:
        - name: X-Org-ID
          in: header
          required: true
          description: Organization ID
          schema:
            type: string
        - name: scope
          in: query
          description: Filter by budget scope
          schema:
            type: string
            enum:
              - organization
              - team
              - agent
              - workflow
              - user
        - name: limit
          in: query
          description: Maximum number of results
          schema:
            type: integer
            default: 50
        - name: offset
          in: query
          description: Offset for pagination
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: List of budgets
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BudgetList'
  /api/v1/budgets/{id}:
    get:
      tags:
        - Cost Controls
      summary: Get a budget
      description: |
        Get a specific budget by ID.

        Enterprise only. These routes are not registered in Community
        edition, which returns 404 Not Found.
      operationId: getBudget
      parameters:
        - name: id
          in: path
          required: true
          description: Budget ID
          schema:
            type: string
      responses:
        '200':
          description: Budget details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Budget'
        '404':
          description: Budget not found
    put:
      tags:
        - Cost Controls
      summary: Update a budget
      description: |
        Update an existing budget configuration.

        Enterprise only. These routes are not registered in Community
        edition, which returns 404 Not Found.
      operationId: updateBudget
      parameters:
        - name: id
          in: path
          required: true
          description: Budget ID
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BudgetUpdate'
      responses:
        '200':
          description: Budget updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Budget'
        '400':
          description: Invalid budget configuration
        '404':
          description: Budget not found
    delete:
      tags:
        - Cost Controls
      summary: Delete a budget
      description: |
        Delete a budget by ID.

        Enterprise only. These routes are not registered in Community
        edition, which returns 404 Not Found.
      operationId: deleteBudget
      parameters:
        - name: id
          in: path
          required: true
          description: Budget ID
          schema:
            type: string
      responses:
        '204':
          description: Budget deleted
        '404':
          description: Budget not found
  /api/v1/budgets/{id}/status:
    get:
      tags:
        - Cost Controls
      summary: Get budget status
      description: |
        Get real-time status of a budget including current usage,
        remaining amount, and whether the budget is exceeded.

        Enterprise only. These routes are not registered in Community
        edition, which returns 404 Not Found.
      operationId: getBudgetStatus
      parameters:
        - name: id
          in: path
          required: true
          description: Budget ID
          schema:
            type: string
      responses:
        '200':
          description: Budget status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BudgetStatus'
              example:
                budget:
                  id: monthly-budget
                  name: Monthly Production Budget
                  scope: organization
                  limit_usd: 1000
                  period: monthly
                used_usd: 450.25
                remaining_usd: 549.75
                percentage: 45.025
                period_start: '2026-01-01T00:00:00Z'
                period_end: '2026-02-01T00:00:00Z'
                is_exceeded: false
                is_blocked: false
        '404':
          description: Budget not found
  /api/v1/budgets/{id}/alerts:
    get:
      tags:
        - Cost Controls
      summary: Get budget alerts
      description: |
        Get alerts triggered for a specific budget.

        Enterprise only. These routes are not registered in Community
        edition, which returns 404 Not Found.
      operationId: getBudgetAlerts
      parameters:
        - name: id
          in: path
          required: true
          description: Budget ID
          schema:
            type: string
        - name: limit
          in: query
          description: Maximum number of alerts to return
          schema:
            type: integer
            default: 50
      responses:
        '200':
          description: Budget alerts
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BudgetAlertList'
        '404':
          description: Budget not found
  /api/v1/budgets/check:
    post:
      tags:
        - Cost Controls
      summary: Check budget before request
      description: |
        Check if a request should be allowed based on budget constraints.
        Returns whether the request is allowed and the applicable budget status.

        Enterprise only. These routes are not registered in Community
        edition, which returns 404 Not Found.
      operationId: checkBudget
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BudgetCheckRequest'
            example:
              org_id: your-org-id
              team_id: engineering
              agent_id: support-bot
      responses:
        '200':
          description: Budget check result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BudgetCheckResponse'
              examples:
                allowed:
                  summary: Request allowed
                  value:
                    allowed: true
                blocked:
                  summary: Request blocked
                  value:
                    allowed: false
                    action: block
                    budget_id: team-budget
                    budget_name: Engineering Team Budget
                    used_usd: 520
                    limit_usd: 500
                    percentage: 104
                    message: >-
                      Budget 'Engineering Team Budget' exceeded - requests
                      blocked
  /api/v1/usage:
    get:
      tags:
        - Cost Controls
      summary: Get usage summary
      description: >
        Get aggregated LLM usage for the current period.


        Available in both Community and Enterprise editions. Basic usage
        overview.
      operationId: getUsageSummary
      parameters:
        - name: X-Org-ID
          in: header
          required: true
          description: Organization ID
          schema:
            type: string
        - name: period
          in: query
          description: Time period for aggregation
          schema:
            type: string
            enum:
              - daily
              - weekly
              - monthly
              - quarterly
              - yearly
            default: monthly
      responses:
        '200':
          description: Usage summary
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UsageSummary'
              example:
                total_cost_usd: 450.25
                total_tokens_in: 1250000
                total_tokens_out: 375000
                total_requests: 5420
                average_cost_per_request: 0.083
  /api/v1/usage/breakdown:
    get:
      tags:
        - Cost Controls
      summary: Get usage breakdown
      description: |
        Get usage broken down by a specific dimension.

        Enterprise only. These routes are not registered in Community
        edition, which returns 404 Not Found.
      operationId: getUsageBreakdown
      parameters:
        - name: X-Org-ID
          in: header
          required: true
          description: Organization ID
          schema:
            type: string
        - name: group_by
          in: query
          required: true
          description: Dimension to group by
          schema:
            type: string
            enum:
              - provider
              - model
              - agent
              - team
              - user
        - name: period
          in: query
          description: Time period for aggregation
          schema:
            type: string
            enum:
              - daily
              - weekly
              - monthly
              - quarterly
              - yearly
            default: monthly
      responses:
        '200':
          description: Usage breakdown
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UsageBreakdown'
              example:
                group_by: provider
                total_cost_usd: 450.25
                items:
                  - group_by: provider
                    group_value: anthropic
                    cost_usd: 320.5
                    tokens_in: 890000
                    tokens_out: 245000
                    request_count: 3200
                    percentage: 71.2
                  - group_by: provider
                    group_value: openai
                    cost_usd: 129.75
                    tokens_in: 360000
                    tokens_out: 130000
                    request_count: 2220
                    percentage: 28.8
  /api/v1/usage/records:
    get:
      tags:
        - Cost Controls
      summary: List usage records
      description: |
        List individual usage records with filtering.

        Enterprise only. These routes are not registered in Community
        edition, which returns 404 Not Found.
      operationId: listUsageRecords
      parameters:
        - name: X-Org-ID
          in: header
          required: true
          description: Organization ID
          schema:
            type: string
        - name: start_time
          in: query
          description: Filter records after this time
          schema:
            type: string
            format: date-time
        - name: end_time
          in: query
          description: Filter records before this time
          schema:
            type: string
            format: date-time
        - name: provider
          in: query
          description: Filter by LLM provider
          schema:
            type: string
        - name: model
          in: query
          description: Filter by model name
          schema:
            type: string
        - name: agent_id
          in: query
          description: Filter by agent ID
          schema:
            type: string
        - name: limit
          in: query
          description: Maximum number of records
          schema:
            type: integer
            default: 100
        - name: offset
          in: query
          description: Offset for pagination
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Usage records
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UsageRecordList'
  /api/v1/pricing:
    get:
      tags:
        - Cost Controls
      summary: Get model pricing
      description: |
        Get pricing information for LLM models.

        Available in both Community and Enterprise editions.
      operationId: getPricing
      parameters:
        - name: provider
          in: query
          description: Filter by provider name
          schema:
            type: string
        - name: model
          in: query
          description: Filter by model name
          schema:
            type: string
      responses:
        '200':
          description: Pricing information
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/PricingInfo'
                  - $ref: '#/components/schemas/PricingList'
              examples:
                single_model:
                  summary: Single model pricing
                  value:
                    provider: anthropic
                    model: claude-sonnet-4
                    pricing:
                      input_per_1k: 0.003
                      output_per_1k: 0.015
                all_providers:
                  summary: All providers pricing
                  value:
                    providers:
                      anthropic:
                        claude-sonnet-4:
                          input_per_1k: 0.003
                          output_per_1k: 0.015
                        claude-opus-4:
                          input_per_1k: 0.015
                          output_per_1k: 0.075
                      openai:
                        gpt-4o:
                          input_per_1k: 0.0025
                          output_per_1k: 0.01
  /api/v1/webhooks:
    post:
      tags:
        - Webhooks
      summary: Create webhook subscription
      description: >
        Create a new webhook subscription to receive real-time event
        notifications.

        Events are delivered as HTTP POST requests to the specified URL with
        HMAC-SHA256

        signatures when a secret is provided.
      operationId: createWebhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWebhookRequest'
            example:
              url: https://example.com/webhooks/axonflow
              events:
                - policy.violation
                - workflow.completed
                - budget.exceeded
              secret: whsec_example_placeholder
              active: true
      responses:
        '201':
          description: Webhook subscription created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookSubscription'
              example:
                id: wh_abc123
                url: https://example.com/webhooks/axonflow
                events:
                  - policy.violation
                  - workflow.completed
                  - budget.exceeded
                active: true
                tenant_id: tenant-1
                org_id: org-1
                secret: whsec_example_placeholder
                created_at: '2026-01-17T10:00:00Z'
                updated_at: '2026-01-17T10:00:00Z'
        '400':
          $ref: '#/components/responses/BadRequest'
    get:
      tags:
        - Webhooks
      summary: List webhook subscriptions
      description: List all webhook subscriptions for the current tenant
      operationId: listWebhooks
      responses:
        '200':
          description: List of webhook subscriptions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListWebhookSubscriptionsResponse'
              example:
                subscriptions:
                  - id: wh_abc123
                    url: https://example.com/webhooks/axonflow
                    events:
                      - policy.violation
                      - workflow.completed
                    active: true
                    tenant_id: tenant-1
                    org_id: org-1
                    created_at: '2026-01-17T10:00:00Z'
                    updated_at: '2026-01-17T10:00:00Z'
                total: 1
  /api/v1/webhooks/{id}:
    get:
      tags:
        - Webhooks
      summary: Get webhook subscription
      description: Retrieve a specific webhook subscription by ID
      operationId: getWebhook
      parameters:
        - name: id
          in: path
          required: true
          description: Webhook subscription ID
          schema:
            type: string
          example: wh_abc123
      responses:
        '200':
          description: Webhook subscription details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookSubscription'
        '404':
          $ref: '#/components/responses/NotFound'
    put:
      tags:
        - Webhooks
      summary: Update webhook subscription
      description: Update an existing webhook subscription
      operationId: updateWebhook
      parameters:
        - name: id
          in: path
          required: true
          description: Webhook subscription ID
          schema:
            type: string
          example: wh_abc123
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateWebhookRequest'
            example:
              url: https://example.com/webhooks/axonflow-v2
              events:
                - policy.violation
                - workflow.completed
                - workflow.failed
              active: true
      responses:
        '200':
          description: Webhook subscription updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookSubscription'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
        - Webhooks
      summary: Delete webhook subscription
      description: >-
        Delete a webhook subscription. Events will no longer be delivered to
        this URL.
      operationId: deleteWebhook
      parameters:
        - name: id
          in: path
          required: true
          description: Webhook subscription ID
          schema:
            type: string
          example: wh_abc123
      responses:
        '204':
          description: Webhook subscription deleted
        '404':
          $ref: '#/components/responses/NotFound'
  /api/v1/unified/executions:
    get:
      tags:
        - Unified Executions
      summary: List executions (MAP plans + WCP workflows)
      description: |
        Lists unified executions across both MAP plans and WCP workflows,
        scoped to the tenant/org identified by the `X-Tenant-ID` and
        `X-Org-ID` headers. The page size is capped by the license tier's
        execution-history limit (at most 100 per page); out-of-range `limit`
        or `offset` values are silently ignored and the defaults used.
      operationId: listUnifiedExecutions
      parameters:
        - $ref: '#/components/parameters/TenantIDHeader'
        - name: X-Org-ID
          in: header
          required: true
          description: Organization identifier for scoping results
          schema:
            type: string
        - name: limit
          in: query
          required: false
          description: >-
            Page size (default 20, max 100 or the tier history cap, whichever is
            lower)
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
        - name: offset
          in: query
          required: false
          description: Number of records to skip
          schema:
            type: integer
            minimum: 0
            default: 0
        - name: execution_type
          in: query
          required: false
          description: Filter by execution type
          schema:
            type: string
            enum:
              - map_plan
              - wcp_workflow
        - name: status
          in: query
          required: false
          description: Filter by execution status
          schema:
            type: string
            enum:
              - pending
              - running
              - completed
              - failed
              - cancelled
              - aborted
              - expired
      responses:
        '200':
          description: Execution list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnifiedExecutionListResponse'
        '500':
          description: Failed to list executions
  /api/v1/unified/executions/{id}:
    get:
      tags:
        - Unified Executions
      summary: Get unified execution status
      description: |
        Returns the unified status record for a MAP plan or WCP workflow
        execution. The id is resolved across both subsystems (direct
        execution id, `plan_...` plan ids, `wf_`/`wcp_` workflow ids).
        Requires both `X-Tenant-ID` and `X-Org-ID`; a tenant/org mismatch
        returns 404 (not 403) to avoid a cross-tenant existence oracle.
      operationId: getUnifiedExecutionStatus
      parameters:
        - name: id
          in: path
          required: true
          description: Execution id (or plan_/wf_/wcp_ prefixed subsystem id)
          schema:
            type: string
        - $ref: '#/components/parameters/TenantIDHeader'
        - name: X-Org-ID
          in: header
          required: true
          description: Organization identifier for scoping results
          schema:
            type: string
      responses:
        '200':
          description: Unified execution status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnifiedExecutionStatus'
        '400':
          description: Missing execution id
        '401':
          description: Missing tenant or org identity headers
        '404':
          description: Execution not found (or belongs to another tenant/org)
        '500':
          description: Failed to resolve execution
  /api/v1/unified/executions/{id}/cancel:
    post:
      tags:
        - Unified Executions
      summary: Cancel a unified execution
      description: |
        Cancels a running MAP plan or WCP workflow through the unified API.
        The cancellation propagates to the owning subsystem (plan cancel or
        workflow abort). Cancelling an execution already in a terminal state
        returns 409.
      operationId: cancelUnifiedExecution
      parameters:
        - name: id
          in: path
          required: true
          description: Execution id (or plan_/wf_/wcp_ prefixed subsystem id)
          schema:
            type: string
        - $ref: '#/components/parameters/TenantIDHeader'
        - name: X-Org-ID
          in: header
          required: true
          description: Organization identifier for scoping results
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                reason:
                  type: string
                  description: >-
                    Cancellation reason (defaults to "cancelled via unified
                    API")
      responses:
        '200':
          description: |
            Execution cancelled; returns the refreshed unified status record
            (or a `{execution_id, status: "cancelled", message}` fallback if
            the post-cancel re-read fails).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnifiedExecutionStatus'
        '400':
          description: Missing execution id or unknown execution type
        '401':
          description: Missing tenant or org identity headers
        '404':
          description: Execution not found (or belongs to another tenant/org)
        '409':
          description: Execution is already in a terminal state
        '500':
          description: Cancellation failed
  /api/v1/unified/executions/{id}/stream:
    get:
      tags:
        - Unified Executions
      summary: Stream execution status via SSE
      description: |
        Server-Sent Events stream for real-time execution status updates.
        Streams events for both MAP plan executions and WCP workflow executions.
        Community: Limited to 5 concurrent connections per tenant.
        Enterprise: Unlimited concurrent connections.
      operationId: streamExecutionStatus
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: X-Tenant-ID
          in: header
          required: true
          schema:
            type: string
        - name: X-Org-ID
          in: header
          required: true
          description: |
            Organization identifier. Like X-Tenant-ID, required by the
            unified-execution tenant-ownership check; missing either header
            is a 401.
          schema:
            type: string
      responses:
        '200':
          description: SSE stream of execution events
          content:
            text/event-stream:
              schema:
                type: string
        '429':
          description: Too many concurrent connections (Community edition limit)
  /api/v1/media-governance/config:
    get:
      tags:
        - Media Governance
      summary: Get media governance configuration
      description: Returns the media governance configuration for the current tenant.
      operationId: getMediaGovernanceConfig
      security:
        - BearerAuth: []
      responses:
        '200':
          description: Media governance configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaGovernanceConfig'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'
    put:
      tags:
        - Media Governance
      summary: Update media governance configuration
      description: |
        Updates media governance configuration. Enterprise tier only.
        Community and Evaluation tiers receive a 403 TIER_RESTRICTED response.
        To toggle system media policies on/off (available to all tiers), use the
        Dynamic Policy API (/api/v1/dynamic-policies) instead.
      operationId: updateMediaGovernanceConfig
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MediaGovernanceConfigUpdate'
      responses:
        '200':
          description: Updated configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaGovernanceConfig'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '500':
          $ref: '#/components/responses/InternalError'
  /api/v1/media-governance/status:
    get:
      tags:
        - Media Governance
      summary: Get media governance feature status
      description: >-
        Returns the media governance feature availability for the current
        license tier.
      operationId: getMediaGovernanceStatus
      security:
        - BearerAuth: []
      responses:
        '200':
          description: Media governance status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaGovernanceStatus'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalError'
  /api/v1/media-governance/audit/export:
    get:
      tags:
        - Media Governance
      summary: Export media governance audit trail (Enterprise)
      description: |
        Exports the media governance audit trail (image analyses, policy
        actions, block decisions) for the tenant as JSON or CSV.

        **Enterprise only** — non-paid tiers receive 403
        (`ENTERPRISE_REQUIRED`). Tenant scope comes from the `X-Tenant-ID`
        header. The export is capped at 10,000 rows, newest first.
      operationId: exportMediaGovernanceAudit
      parameters:
        - $ref: '#/components/parameters/TenantIDHeader'
        - name: from
          in: query
          required: false
          description: Window start (RFC3339). Defaults to 7 days ago.
          schema:
            type: string
            format: date-time
        - name: to
          in: query
          required: false
          description: Window end (RFC3339). Defaults to now.
          schema:
            type: string
            format: date-time
        - name: format
          in: query
          required: false
          description: Export format
          schema:
            type: string
            enum:
              - json
              - csv
            default: json
      responses:
        '200':
          description: |
            Audit export. CSV columns: request_id, tenant_id, timestamp,
            media_type, blocked, policy_actions (JSON-encoded array).
          content:
            application/json:
              schema:
                type: object
                properties:
                  records:
                    type: array
                    items:
                      $ref: '#/components/schemas/MediaAuditRecord'
                  tenant_id:
                    type: string
                  from:
                    type: string
                    format: date-time
                  to:
                    type: string
                    format: date-time
                  count:
                    type: integer
            text/csv:
              schema:
                type: string
        '400':
          description: Missing X-Tenant-ID (MISSING_TENANT_ID) or invalid from/to/format
        '403':
          description: Requires Enterprise license (ENTERPRISE_REQUIRED)
        '500':
          description: Query failure (QUERY_ERROR)
  /api/v1/policies/simulate:
    post:
      tags:
        - Policy Simulation
      summary: Simulate all active policies (Evaluation+)
      description: >
        Runs all active policies against the provided input as a dry run.

        No audit writes or action application. Requires Evaluation or Enterprise
        license.
      operationId: simulatePolicies
      security:
        - BearerAuth: []
        - basicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - query
              properties:
                query:
                  type: string
                  description: The input text to simulate against all policies
                request_type:
                  type: string
                  description: Request type (defaults to "simulation")
                user:
                  $ref: '#/components/schemas/UserContext'
                client:
                  $ref: '#/components/schemas/ClientContext'
                context:
                  type: object
                  additionalProperties: true
      responses:
        '200':
          description: Simulation results
          content:
            application/json:
              schema:
                type: object
                properties:
                  allowed:
                    type: boolean
                  applied_policies:
                    type: array
                    items:
                      type: string
                  risk_score:
                    type: number
                  required_actions:
                    type: array
                    items:
                      type: string
                  processing_time_ms:
                    type: integer
                  total_policies:
                    type: integer
                    description: |
                      Number of active policies visible to the calling tenant —
                      its own plus the shared global/default baseline.

                      CHANGED: this previously counted every active policy in
                      the deployment, across all tenants, which disclosed the
                      deployment-wide policy count to every caller.
                  dry_run:
                    type: boolean
                    enum:
                      - true
                  simulated_at:
                    type: string
                    format: date-time
                  tier:
                    type: string
                  daily_usage:
                    type: object
                    properties:
                      used:
                        type: integer
                      limit:
                        type: integer
        '403':
          description: Feature requires Evaluation or Enterprise license
        '429':
          description: Daily simulation limit exceeded
  /api/v1/policies/impact-report:
    post:
      tags:
        - Policy Simulation
      summary: Generate impact report for a policy (Evaluation+)
      description: >
        Tests a single policy against multiple inputs and returns aggregate
        statistics.

        Requires Evaluation or Enterprise license.
      operationId: generateImpactReport
      security:
        - BearerAuth: []
        - basicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - policy_id
                - inputs
              properties:
                policy_id:
                  type: string
                inputs:
                  type: array
                  items:
                    type: object
                    required:
                      - query
                    properties:
                      query:
                        type: string
                      request_type:
                        type: string
                      user:
                        type: object
                        additionalProperties: true
                      context:
                        type: object
                        additionalProperties: true
      responses:
        '200':
          description: Impact report results
          content:
            application/json:
              schema:
                type: object
                properties:
                  policy_id:
                    type: string
                  total_inputs:
                    type: integer
                  matched:
                    type: integer
                  blocked:
                    type: integer
                  match_rate:
                    type: number
                  block_rate:
                    type: number
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        input_index:
                          type: integer
                        matched:
                          type: boolean
                        blocked:
                          type: boolean
                        actions:
                          type: array
                          items:
                            type: string
                  processing_time_ms:
                    type: integer
                  generated_at:
                    type: string
                    format: date-time
                  tier:
                    type: string
        '400':
          description: Bad request (missing fields or input limit exceeded)
        '403':
          description: Feature requires Evaluation or Enterprise license
  /api/v1/policies/conflicts:
    post:
      tags:
        - Policy Simulation
      summary: Detect policy conflicts (Evaluation+)
      description: |
        Analyzes active policies for contradictions, shadows, and redundancies.
        Optionally filter to conflicts involving a specific policy.
        Requires Evaluation or Enterprise license.
      operationId: detectPolicyConflicts
      security:
        - BearerAuth: []
        - basicAuth: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                policy_id:
                  type: string
                  description: Optional — filter to conflicts involving this policy
      responses:
        '200':
          description: Conflict analysis results
          content:
            application/json:
              schema:
                type: object
                properties:
                  conflicts:
                    type: array
                    items:
                      type: object
                      properties:
                        policy_a:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                            type:
                              type: string
                        policy_b:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                            type:
                              type: string
                        conflict_type:
                          type: string
                          enum:
                            - contradictory_action
                            - shadow
                            - redundant
                        description:
                          type: string
                        severity:
                          type: string
                          enum:
                            - high
                            - medium
                            - low
                        overlapping_field:
                          type: string
                  total_policies:
                    type: integer
                  conflict_count:
                    type: integer
                  checked_at:
                    type: string
                    format: date-time
                  tier:
                    type: string
        '403':
          description: Feature requires Evaluation or Enterprise license
        '429':
          description: Daily simulation limit exceeded
  /api/v1/evidence/export:
    post:
      tags:
        - Evidence Export
      summary: Export evidence pack (Evaluation+)
      description: >
        Exports audit logs, workflow steps, and HITL approvals as a bundled JSON
        pack.

        Evaluation tier exports include a "NOT FOR REGULATORY SUBMISSION"
        watermark.

        Enterprise exports are clean (no watermark).
      operationId: exportEvidence
      security:
        - BearerAuth: []
        - basicAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - start_date
              properties:
                start_date:
                  type: string
                  description: Start date (YYYY-MM-DD or RFC3339)
                end_date:
                  type: string
                  description: End date (defaults to now)
                types:
                  type: array
                  items:
                    type: string
                    enum:
                      - audit_logs
                      - workflow_steps
                      - hitl_approvals
                  description: Evidence types to include (defaults to all)
                limit:
                  type: integer
                  description: Maximum records (capped by tier limit)
      responses:
        '200':
          description: Evidence export bundle
          content:
            application/json:
              schema:
                type: object
                properties:
                  export_id:
                    type: string
                  tenant_id:
                    type: string
                  tier:
                    type: string
                  date_range:
                    type: object
                    properties:
                      start:
                        type: string
                        format: date-time
                      end:
                        type: string
                        format: date-time
                  disclaimer:
                    type: string
                    description: Watermark for Evaluation tier exports
                  record_count:
                    type: integer
                  audit_logs:
                    type: array
                    items:
                      type: object
                  workflow_steps:
                    type: array
                    items:
                      type: object
                  hitl_approvals:
                    type: array
                    items:
                      type: object
                  exported_at:
                    type: string
                    format: date-time
                  daily_usage:
                    type: object
                    properties:
                      used:
                        type: integer
                      limit:
                        type: integer
        '403':
          description: Feature requires Evaluation or Enterprise license
        '429':
          description: Daily export limit exceeded
  /api/v1/evidence/summary:
    get:
      tags:
        - Evidence Export
      summary: Get evidence summary (Evaluation+)
      description: >
        Returns counts of evidence records by type within the tier's lookback
        window.
      operationId: getEvidenceSummary
      security:
        - BearerAuth: []
        - basicAuth: []
      responses:
        '200':
          description: Evidence summary
          content:
            application/json:
              schema:
                type: object
                properties:
                  tenant_id:
                    type: string
                  tier:
                    type: string
                  window_days:
                    type: integer
                  counts:
                    type: object
                    properties:
                      audit_logs:
                        type: integer
                      workflow_steps:
                        type: integer
                      hitl_approvals:
                        type: integer
                      total:
                        type: integer
                  generated_at:
                    type: string
                    format: date-time
                  disclaimer:
                    type: string
        '403':
          description: Feature requires Evaluation or Enterprise license
components:
  headers:
    XAxonflowReadScope:
      description: |
        RBAC read-scope diagnostic. Echoes the read scope the orchestrator
        resolved server-side for this caller and applied to the query:

        - `tenant` — the caller may read every user's rows within the
          (header-forced) tenant: a validated `admin`/`owner` role over the
          internal proxy-auth channel, the customer-portal's tenant-scope
          assertion, or a single-operator deployment (`DEPLOYMENT_MODE=community`,
          and `community-saas` for requests proven to have arrived over the
          agent gateway — there the organization, tenant and credential are one
          `cs_<uuid>`, so "tenant-wide" is that one evaluator's own data).
        - `own-rows` — the caller has a validated per-user identity but no
          tenant-wide authority, so the read was restricted to rows stamped
          with their own canonical `user_email`.
        - `none` — fail-closed. The caller presented neither tenant-wide
          authority nor a per-user identity, so **zero rows** were returned.
          The orchestrator also logs one diagnostic line on this path.

        The header exists so that a `200` with an empty page — or a `404` on a
        record that does exist — is distinguishable from a genuinely empty
        trail. That ambiguity is what let a whole deployment read zero rows
        without anyone noticing.

        **Diagnostic only.** The response body is byte-for-byte unchanged by
        its presence or value, and nothing keys authorization off it. It is a
        RESPONSE header the orchestrator writes, never an input: a
        client-supplied `X-Axonflow-Read-Scope` REQUEST header is stripped by
        the agent gateway and is never trusted. Scope is derived from the
        validated identity carried over the internal agent→orchestrator
        proxy-auth channel.

        Read scope is also a separate axis from administrative authority.
        `tenant` here does not imply the caller may run whole-tenant compliance
        exports or the cost/usage/execution family — those stay admin-gated and
        still return 403.

        Stamped before the handler writes its status line, so it is present on
        any response produced after the scope is resolved (including the
        deliberately non-oracle `404`s), and absent on requests rejected before
        that point (e.g. a missing `X-Tenant-ID`).
      schema:
        type: string
        enum:
          - tenant
          - own-rows
          - none
        example: tenant
  parameters:
    TenantIDHeader:
      name: X-Tenant-ID
      in: header
      required: true
      description: |
        Tenant identifier scoping the request. The AxonFlow Agent gateway
        sets this header after authentication; the orchestrator fails closed
        when it is absent (401 on audit/decision endpoints, 400 on others —
        see each operation). Clients cannot widen their scope through it:
        handlers force the tenant filter from this header, never from the
        request body.
      schema:
        type: string
      example: travel-us
    OrgIDHeader:
      name: X-Org-ID
      in: header
      required: false
      description: |
        Organization identifier. Falls back to authenticated org from
        the Basic auth client when omitted. Surface for callers that
        need to override (e.g. cross-org admin reads in Enterprise).
      schema:
        type: string
      example: travel-us
    UserIDHeader:
      name: X-User-ID
      in: header
      required: false
      description: |
        Caller identity. Defaults to the authenticated user resolved
        from the request token when omitted. Surface for service-mode
        callers that proxy on behalf of multiple users.
      schema:
        type: string
    LimitParam:
      name: limit
      in: query
      required: false
      description: |
        Maximum number of records to return. Defaults vary by
        endpoint; see per-endpoint description for the cap.
      schema:
        type: integer
        minimum: 1
        default: 100
    OffsetParam:
      name: offset
      in: query
      required: false
      description: |
        Number of records to skip from the start of the result set.
        Pair with `limit` to walk multi-page reads.
      schema:
        type: integer
        minimum: 0
        default: 0
  schemas:
    HealthResponse:
      type: object
      description: |
        Besides the component booleans, the health body always carries the
        platform capability list (`capabilities`), the per-language SDK
        version pins (`sdk_compatibility`) and the per-plugin version pins
        (`plugin_compatibility`). The pin maps are additionalProperties-style
        maps keyed by SDK language / canonical plugin id; the exact key sets
        evolve per release and are not enumerated here.
      properties:
        status:
          type: string
          enum:
            - healthy
            - starting
            - unhealthy
        service:
          type: string
        version:
          type: string
          description: |
            Platform version, preferring the value baked into the binary at
            build time and falling back to AXONFLOW_VERSION for dev builds
          example: 9.8.0
        timestamp:
          type: string
          format: date-time
        capabilities:
          type: array
          description: List of platform capabilities with the version that introduced them
          items:
            $ref: '#/components/schemas/PlatformCapability'
        sdk_compatibility:
          $ref: '#/components/schemas/SDKCompatInfo'
        plugin_compatibility:
          $ref: '#/components/schemas/PluginCompatInfo'
        components:
          type: object
          properties:
            policy_engine:
              type: boolean
            llm_router:
              type: boolean
            response_processor:
              type: boolean
            audit_logger:
              type: boolean
            workflow_engine:
              type: boolean
            planning_engine:
              type: boolean
            result_aggregator:
              type: boolean
        features:
          type: object
          properties:
            multi_agent_planning:
              type: boolean
    PlatformCapability:
      type: object
      properties:
        name:
          type: string
          description: Capability identifier
          example: workflow_control
        since:
          type: string
          description: Platform version that introduced this capability
          example: 4.3.0
        description:
          type: string
          description: Human-readable description
          example: WCP workflow lifecycle management
    SDKCompatInfo:
      type: object
      properties:
        min_sdk_version:
          type: object
          description: |
            Minimum SDK version compatible with this platform, keyed by SDK
            language (e.g. go, python, typescript, java, rust)
          additionalProperties:
            type: string
          example:
            python: 8.0.0
        recommended_sdk_version:
          type: object
          description: |
            Recommended SDK version for full feature coverage, keyed by SDK
            language
          additionalProperties:
            type: string
          example:
            python: 9.0.0
    PluginCompatInfo:
      type: object
      description: |
        Plugin version pins, mirroring SDKCompatInfo. Keys are the canonical
        plugin ids (e.g. openclaw, claude-code, cursor, codex,
        claude-desktop). Versions below the minimum receive a
        downgrade-warning header on every governed call.
      properties:
        min_plugin_version:
          type: object
          additionalProperties:
            type: string
        recommended_plugin_version:
          type: object
          additionalProperties:
            type: string
    PlanStatusResponse:
      type: object
      description: >
        Unified execution status for MAP plans. Provides step-level progress
        tracking,

        duration, and cost information. Compatible with the unified
        ExecutionStatus schema.
      required:
        - plan_id
        - status
        - query
        - domain
        - total_steps
      properties:
        plan_id:
          type: string
          description: Original plan identifier
          example: plan_1705312200_abc123
        execution_id:
          type: string
          description: Unified execution ID for cross-system tracking
          example: plan_xyz789
        status:
          type: string
          enum:
            - pending
            - executing
            - completed
            - failed
            - expired
            - cancelled
            - aborted
            - awaiting_approval
          description: Current execution status
          example: executing
        query:
          type: string
          description: Original user query that created the plan
          example: Research competitor pricing strategies
        domain:
          type: string
          description: Domain classification of the plan
          example: finance
        total_steps:
          type: integer
          description: Total number of steps in the plan
          example: 3
        completed_steps:
          type: integer
          description: Number of completed steps
          example: 1
        progress_percent:
          type: number
          format: float
          description: Completion percentage (0-100)
          example: 33.33
        duration:
          type: string
          description: Human-readable elapsed duration
          example: 15s
        estimated_cost_usd:
          type: number
          format: float
          description: Estimated total cost in USD
          example: 0.05
        actual_cost_usd:
          type: number
          format: float
          description: Actual cost incurred so far in USD
          example: 0.042
        created_at:
          type: string
          format: date-time
          description: When the plan was created
        started_at:
          type: string
          format: date-time
          description: When execution started
        completed_at:
          type: string
          format: date-time
          description: When execution completed (if terminal)
        expires_at:
          type: string
          format: date-time
          description: When the plan expires
        error:
          type: string
          description: Error message if status is failed
        steps:
          type: array
          description: Detailed status of each step
          items:
            $ref: '#/components/schemas/StepStatus'
        metadata:
          type: object
          description: Additional plan metadata
          additionalProperties: true
    StepStatus:
      type: object
      description: Status of an individual execution step
      required:
        - step_id
        - step_index
        - step_name
        - status
      properties:
        step_id:
          type: string
          description: Unique step identifier
          example: step_0_analyze
        step_index:
          type: integer
          description: Position in the execution sequence (0-based)
          example: 0
        step_name:
          type: string
          description: Human-readable step name
          example: analyze
        step_type:
          type: string
          enum:
            - llm_call
            - tool_call
            - connector_call
            - human_task
            - synthesis
            - action
            - gate
          description: Type of step operation
          example: llm_call
        status:
          type: string
          enum:
            - pending
            - running
            - completed
            - failed
            - skipped
            - blocked
            - approval
          description: Current step status
          example: completed
        duration:
          type: string
          description: Human-readable step duration
          example: 8s
        started_at:
          type: string
          format: date-time
          description: When the step started
        ended_at:
          type: string
          format: date-time
          description: When the step ended
        model:
          type: string
          description: LLM model used (for llm_call steps)
          example: gpt-4
        provider:
          type: string
          description: LLM provider (for llm_call steps)
          example: openai
        cost_usd:
          type: number
          format: float
          description: Cost of this step in USD
          example: 0.04
        input_tokens:
          type: integer
          description: Number of input tokens (for LLM steps)
        output_tokens:
          type: integer
          description: Number of output tokens (for LLM steps)
        error:
          type: string
          description: Error message if step failed
    MetricsResponse:
      type: object
      properties:
        orchestrator_metrics:
          type: object
          properties:
            uptime_seconds:
              type: number
            total_requests:
              type: integer
            success_requests:
              type: integer
            failed_requests:
              type: integer
            blocked_requests:
              type: integer
            success_rate:
              type: number
            rps:
              type: number
            error_rate_per_sec:
              type: number
            dynamic_policy_eval_p50_ms:
              type: number
            dynamic_policy_eval_p95_ms:
              type: number
            dynamic_policy_eval_p99_ms:
              type: number
            llm_routing_p50_ms:
              type: number
            llm_routing_p95_ms:
              type: number
            llm_routing_p99_ms:
              type: number
        health:
          type: object
          properties:
            up:
              type: integer
            consecutive_errors:
              type: integer
        request_types:
          type: object
          additionalProperties:
            type: object
        providers:
          type: object
          additionalProperties:
            type: object
            properties:
              total_calls:
                type: integer
              success_calls:
                type: integer
              failed_calls:
                type: integer
              total_tokens:
                type: integer
              total_cost:
                type: number
              p99_ms:
                type: number
        timestamp:
          type: string
          format: date-time
    OrchestratorRequest:
      type: object
      required:
        - query
        - user
        - client
      properties:
        request_id:
          type: string
          description: Unique request identifier
        query:
          type: string
          description: Query to process
        request_type:
          type: string
          description: Type of request
        skip_llm:
          type: boolean
          default: false
          description: Skip LLM calls (for testing)
        user:
          $ref: '#/components/schemas/UserContext'
        client:
          $ref: '#/components/schemas/ClientContext'
        context:
          type: object
          description: >
            Free-form request metadata. Routing controls:

            - `provider` (string): preferred provider

            - `strict_provider` (boolean, optional): when true, hard-pins
            `provider` and disables fallback
              for this request. Default is false unless server env `LLM_STRICT_PROVIDER_DEFAULT=true`.
          additionalProperties: true
        timestamp:
          type: string
          format: date-time
        media:
          type: array
          items:
            $ref: '#/components/schemas/MediaContentRequest'
          maxItems: 10
          description: Optional media content (images) for multimodal governance analysis
    UserContext:
      type: object
      properties:
        id:
          type: integer
        email:
          type: string
        role:
          type: string
        permissions:
          type: array
          items:
            type: string
        tenant_id:
          type: string
    ClientContext:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        org_id:
          type: string
        tenant_id:
          type: string
    OrchestratorResponse:
      type: object
      properties:
        request_id:
          type: string
        success:
          type: boolean
        data:
          description: Response data
        error:
          type: string
        redacted:
          type: boolean
        redacted_fields:
          type: array
          items:
            type: string
        policy_info:
          $ref: '#/components/schemas/PolicyEvaluationResult'
        provider_info:
          $ref: '#/components/schemas/ProviderInfo'
        processing_time:
          type: string
        media_analysis:
          description: Media governance analysis results (present when media was submitted)
          allOf:
            - $ref: '#/components/schemas/MediaAnalysisResponse'
    PolicyEvaluationResult:
      type: object
      properties:
        allowed:
          type: boolean
        applied_policies:
          type: array
          items:
            type: string
        risk_score:
          type: number
          minimum: 0
          maximum: 1
        required_actions:
          type: array
          items:
            type: string
        processing_time_ms:
          type: integer
        database_accessed:
          type: boolean
    PolicyMatch:
      type: object
      description: Details of a policy match during evaluation (Issue
      properties:
        policy_id:
          type: string
          description: Unique identifier for the policy
        policy_name:
          type: string
          description: Human-readable name of the policy
        action:
          type: string
          description: Action taken by this policy
          enum:
            - allow
            - block
            - require_approval
            - redact
        reason:
          type: string
          description: Reason for the policy match
    ProviderInfo:
      type: object
      properties:
        provider:
          type: string
          enum:
            - openai
            - azure-openai
            - anthropic
            - bedrock
            - ollama
            - gemini
            - mock
        model:
          type: string
        response_time_ms:
          type: integer
        tokens_used:
          type: integer
        cost:
          type: number
    PlanRequest:
      type: object
      required:
        - query
        - user
      properties:
        query:
          type: string
          description: Natural language task description
        domain:
          type: string
          enum:
            - travel
            - healthcare
            - finance
            - generic
          default: generic
          description: Task domain for specialized handling
        execution_mode:
          type: string
          enum:
            - auto
            - parallel
            - sequential
            - balanced
            - confirm
            - step
          default: auto
          description: >
            How to execute sub-tasks.

            - `auto`: Automatically determines parallel/sequential (recommended)

            - `parallel`: Force parallel execution of all independent steps

            - `sequential`: Force sequential step-by-step execution

            - `balanced`: I/O-bound connector steps parallel, LLM steps
            sequential

            - `confirm`: Every step requires explicit approval before execution
            (Enterprise only)

            - `step`: First step auto-executes, subsequent steps require
            approval (Enterprise only)
        user:
          $ref: '#/components/schemas/UserContext'
        client:
          type: object
          additionalProperties: true
        context:
          type: object
          additionalProperties: true
    PlanResponse:
      type: object
      properties:
        success:
          type: boolean
        plan_id:
          type: string
        version:
          type: integer
          description: Plan version number (for optimistic locking)
          example: 1
        steps:
          type: array
          description: Generated plan steps
          items:
            type: object
            properties:
              name:
                type: string
              type:
                type: string
              description:
                type: string
        workflow_execution_id:
          type: string
        result:
          description: Final aggregated result
        metadata:
          type: object
          properties:
            tasks_executed:
              type: integer
            execution_mode:
              type: string
            execution_time_ms:
              type: integer
            tasks:
              type: array
              items:
                type: object
                properties:
                  name:
                    type: string
                  status:
                    type: string
                  time_ms:
                    type: integer
        error:
          type: string
        policy_info:
          description: Policy evaluation result for the plan execution (Issue
          allOf:
            - $ref: '#/components/schemas/PolicyEvaluationResult'
        complexity:
          type: string
          description: |
            Plan complexity hint inferred from query characteristics
            (e.g. simple/medium/complex). Surfaced from the planning
            engine so callers can apply complexity-aware UI hints.
          example: medium
        domain:
          type: string
          description: |
            Inferred query domain (e.g. travel, finance, healthcare).
            Echoes the request's `domain` field if provided, otherwise
            populated by the planning engine's domain classifier.
          example: travel
        parallel:
          type: boolean
          description: |
            True when the planning engine produced steps that can
            execute in parallel. Callers may choose to render
            parallel-step UIs differently from sequential ones.
        status:
          type: string
          description: |
            Plan lifecycle status (e.g. created, in_progress, completed,
            failed). Mirrors the WorkflowStatusResponse.status field for
            plans the orchestrator returns synchronously alongside the
            plan body.
    CancelPlanResponse:
      type: object
      properties:
        success:
          type: boolean
        plan_id:
          type: string
        status:
          type: string
          enum:
            - cancelled
        message:
          type: string
          description: |
            Human-readable summary of the cancel outcome (e.g.
            "Plan cancelled — 3 of 5 steps had already executed and
            were rolled back"). Useful for end-user UI; programmatic
            callers should prefer `status`.
    UpdatePlanRequest:
      type: object
      required:
        - version
      properties:
        version:
          type: integer
          description: Expected current version (for optimistic locking)
          example: 1
        execution_mode:
          type: string
          enum:
            - auto
            - parallel
            - sequential
            - balanced
            - confirm
            - step
          description: New execution mode
        domain:
          type: string
          description: New domain
        metadata:
          type: object
          additionalProperties: true
          description: Additional metadata to set
    UpdatePlanResponse:
      type: object
      properties:
        success:
          type: boolean
        plan_id:
          type: string
        version:
          type: integer
          description: New version number after update
        status:
          type: string
    PlanVersionEntry:
      type: object
      properties:
        version:
          type: integer
        changed_at:
          type: string
          format: date-time
        changed_by:
          type: string
        change_type:
          type: string
          enum:
            - created
            - updated
            - rollback
        change_summary:
          type: string
    PlanVersionsResponse:
      type: object
      properties:
        plan_id:
          type: string
        versions:
          type: array
          items:
            $ref: '#/components/schemas/PlanVersionEntry'
    ResumePlanResponse:
      type: object
      properties:
        plan_id:
          type: string
        status:
          type: string
          enum:
            - awaiting_approval
            - completed
            - failed
          description: Status after resuming
        result:
          description: Final result if plan completed
        approved:
          type: boolean
          description: |
            True when the resume was driven by an approval; false when
            the resume rejected the pending decision. Mirrors the
            decision the caller submitted on /resume.
        message:
          type: string
          description: |
            Human-readable summary of the resume outcome (e.g.
            "Step 3 approved; plan continuing to step 4").
        next_step:
          type: integer
          description: |
            1-based index of the next step the orchestrator will run
            after the resume completes. Absent when the plan is
            already terminal.
        next_step_name:
          type: string
          description: |
            Name of the step at `next_step` (mirrors PlanStep.name).
            Useful for callers rendering "Continuing to: …" UI.
        step_result:
          description: |
            Result of the step that was waiting on this approval.
            Shape depends on the step type; treat as opaque on the
            client unless the step type is known.
        total_steps:
          type: integer
          description: |
            Total number of steps in the plan, so callers can render
            "Step N of M" without a separate plan-status lookup.
        workflow_id:
          type: string
          description: |
            WCP workflow id this plan is bound to. Surfaced here so
            callers don't need a separate /plans/{id} round-trip after
            resume to learn the workflow id.
    RollbackPlanResponse:
      type: object
      description: Response after rolling back a plan to a previous version
      required:
        - plan_id
        - version
        - previous_version
        - status
      properties:
        plan_id:
          type: string
          description: Plan identifier
          example: plan_1705312200_abc123
        version:
          type: integer
          description: New version number after rollback
          example: 4
        previous_version:
          type: integer
          description: The version that was restored
          example: 2
        status:
          type: string
          description: Plan status after rollback
          enum:
            - pending
            - executing
          example: pending
    ProviderStatusResponse:
      type: object
      properties:
        providers:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              available:
                type: boolean
              models:
                type: array
                items:
                  type: string
              weight:
                type: number
              avg_latency_ms:
                type: integer
    DynamicPolicy:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        enabled:
          type: boolean
        type:
          type: string
          description: |
            Policy type — narrower-than-category dimension surfaced on
            list/CRUD responses (e.g. risk, content, user, cost).
          example: risk
        category:
          type: string
          description: |
            Policy category. Dynamic policies use a `dynamic-` prefix
            (dynamic-risk, dynamic-compliance, dynamic-security,
            dynamic-cost, dynamic-access).
          example: dynamic-risk
        tier:
          type: string
          enum:
            - system
            - organization
            - tenant
          description: |
            Policy tier. Dynamic policies default to `tenant`;
            organization-tier policies require Enterprise.
        organization_id:
          type: string
          nullable: true
          description: |
            Organization id for organization-tier policies. Null for
            system and tenant tiers.
        priority:
          type: integer
          description: |
            Evaluation priority — lower values evaluate first. Useful
            when multiple dynamic policies match the same request.
          example: 0
        created_at:
          type: string
          format: date-time
          description: When the dynamic policy was first inserted.
        updated_at:
          type: string
          format: date-time
          description: When the dynamic policy was last modified.
        conditions:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
              operator:
                type: string
                enum:
                  - '=='
                  - '!='
                  - '>'
                  - <
                  - '>='
                  - <=
                  - contains
                  - matches
              value:
                description: Comparison value
        actions:
          type: array
          items:
            type: object
            properties:
              type:
                type: string
                enum:
                  - block
                  - allow
                  - rate_limit
                  - redact
                  - alert
              reason:
                type: string
              limit:
                type: integer
    Policy:
      type: object
      description: A policy with full metadata
      properties:
        id:
          type: string
          format: uuid
        policy_id:
          type: string
          description: Human-readable identifier
        name:
          type: string
        description:
          type: string
        type:
          type: string
          enum:
            - static
            - dynamic
        category:
          type: string
        tier:
          type: string
          enum:
            - system
            - organization
            - tenant
        pattern:
          type: string
          description: Regex pattern (for static policies)
        action:
          type: string
          enum:
            - block
            - require_approval
            - redact
            - warn
            - log
        severity:
          type: string
          enum:
            - critical
            - high
            - medium
            - low
        priority:
          type: integer
        enabled:
          type: boolean
        conditions:
          type: array
          items:
            type: object
          description: Conditions (for dynamic policies)
        actions:
          type: array
          items:
            type: object
          description: Actions (for dynamic policies)
        version:
          type: integer
        tenant_id:
          type: string
        organization_id:
          type: string
        created_by:
          type: string
        created_at:
          type: string
          format: date-time
        updated_by:
          type: string
        updated_at:
          type: string
          format: date-time
    PoliciesListResponse:
      type: object
      properties:
        policies:
          type: array
          items:
            $ref: '#/components/schemas/Policy'
        pagination:
          type: object
          properties:
            page:
              type: integer
            page_size:
              type: integer
            total_items:
              type: integer
            total_pages:
              type: integer
    CreatePolicyRequest:
      type: object
      required:
        - name
        - type
      properties:
        name:
          type: string
        description:
          type: string
        type:
          type: string
          enum:
            - static
            - dynamic
        category:
          type: string
        tier:
          type: string
          enum:
            - organization
            - tenant
          default: tenant
        pattern:
          type: string
          description: Required for static policies
        action:
          type: string
          enum:
            - block
            - require_approval
            - redact
            - warn
            - log
        severity:
          type: string
          enum:
            - critical
            - high
            - medium
            - low
        priority:
          type: integer
          default: 100
        enabled:
          type: boolean
          default: true
        conditions:
          type: array
          items:
            type: object
          description: Required for dynamic policies
        actions:
          type: array
          items:
            type: object
          description: Required for dynamic policies
    UpdatePolicyRequest:
      type: object
      description: All fields optional for partial update
      properties:
        name:
          type: string
        description:
          type: string
        pattern:
          type: string
        action:
          type: string
          enum:
            - block
            - require_approval
            - redact
            - warn
            - log
        severity:
          type: string
          enum:
            - critical
            - high
            - medium
            - low
        priority:
          type: integer
        enabled:
          type: boolean
        conditions:
          type: array
          items:
            type: object
        actions:
          type: array
          items:
            type: object
    AuditSearchRequest:
      type: object
      description: |
        Search filters. The tenant scope is NOT part of this body — it is
        always forced from the X-Tenant-ID header and cannot be overridden
        by the payload.
      properties:
        user_email:
          type: string
          description: Case-insensitive partial (ILIKE substring) match
        client_id:
          type: string
          description: Case-insensitive partial (ILIKE substring) match
        start_time:
          type: string
          format: date-time
          description: |
            Window lower bound; clamped to the tenant's tier-based
            retention cutoff when earlier (or when omitted)
        end_time:
          type: string
          format: date-time
        action:
          type: string
          description: |
            Filter by policy decision. The value is normalized to its
            canonical verdict (allowed, blocked, redacted, needs_approval,
            error) and expanded to every historical DB spelling of that
            verdict, so it matches both current and legacy rows.
        session_id:
          type: string
          description: |
            Exact match on the first-class session_id column — used to
            drill into a session-summary bucket's raw events (#2857).
        limit:
          type: integer
          default: 100
        offset:
          type: integer
          minimum: 0
          default: 0
          description: |
            Pagination offset — number of audit-log rows to skip from
            the start of the result set. Pair with `limit` to walk
            multi-page audit reads.
        decision_id:
          type: string
          description: |
            Filter audit reads to a specific governance decision id
            (mints from MCPCheckInputResponse / MCPCheckOutputResponse
            etc.). Matches `policy_details->>'decision_id'`. Useful when
            correlating a specific request through its full audit trail.
        override_id:
          type: string
          description: |
            Filter to audit entries that recorded an override-used
            event for this override id (matches
            `policy_details->>'override_id'`).
        policy_name:
          type: string
          description: |
            Filter to audit entries where this policy fired. Matches the
            three shapes audit writers store in the policy_details JSONB:
            the scalar `policy_details.policy_name`, the CSV string
            `policy_details.policy_names`, and
            `policy_details.policy_matches[*].policy_name` (workflow step
            gates + decision records).
    AuditSearchResponse:
      type: object
      description: Paginated audit search results
      properties:
        entries:
          type: array
          description: Matching audit entries (always an array, `[]` when empty)
          items:
            $ref: '#/components/schemas/AuditLogEntry'
        total:
          type: integer
          description: True pre-LIMIT match count for the filters (for pagination)
        limit:
          type: integer
        offset:
          type: integer
    AuditLogEntry:
      type: object
      description: |
        A single audit_logs row as serialized on the wire (orchestrator
        AuditEntry struct). Optional canonical-decision, cross-border and
        session fields are omitted when empty.
      properties:
        id:
          type: string
        request_id:
          type: string
        timestamp:
          type: string
          format: date-time
        user_id:
          type: integer
        user_email:
          type: string
        user_role:
          type: string
        client_id:
          type: string
        tenant_id:
          type: string
        org_id:
          type: string
        request_type:
          type: string
        query:
          type: string
          description: The audited query/prompt (already redacted by the write path)
        query_hash:
          type: string
        policy_decision:
          type: string
          description: |
            Verdict for the request. Canonical values are allowed, blocked,
            redacted, needs_approval, error; historical rows may carry
            legacy spellings.
        policy_details:
          type: object
          description: |
            Nested decision detail exactly as the writer stored it
            (policy_ids / reasons / latency_ms plus writer-specific keys
            like gateway_id, tool_name, decision_id, override_id,
            policy_matches). Treat keys as writer-specific.
          additionalProperties: true
        provider:
          type: string
        model:
          type: string
          description: |
            LLM model identifier the request was routed to (e.g.
            `gpt-4o-mini`, `llama3.2:latest`). Surfaced separately from
            `provider` so callers can filter audit reads by model without
            parsing the provider's vendor-specific naming.
        response_time_ms:
          type: integer
          format: int64
        tokens_used:
          type: integer
        cost:
          type: number
          format: double
        redacted_fields:
          type: array
          items:
            type: string
        error_message:
          type: string
          description: Omitted when empty
        response_sample:
          type: string
        compliance_flags:
          type: array
          items:
            type: string
        security_metrics:
          type: object
          additionalProperties: true
        decision_id:
          type: string
          description: |
            Canonical decision-row id (#2597/ADR-058); present on planes
            that mint decisions, omitted on legacy writers
        plane:
          type: string
          description: >-
            Enforcement plane that wrote the row (e.g. mcp, llm); omitted on
            legacy writers
        correlation_id:
          type: string
          description: Decision-chain correlation key (#2611); omitted when not stitched
        transfer_basis:
          type: string
          description: |
            UU PDP Pasal 56 cross-border transfer legal basis (adequacy,
            safeguards, pasal_56b_dpa, consent) — Enterprise LLM-forward
            path only (#2718)
        data_residency:
          type: string
          description: ISO 3166-1 alpha-2 destination country (#2718); Enterprise only
        session_id:
          type: string
          description: |
            AI-tool session id (Claude Code / Desktop) forwarded via
            X-Session-Id; asserted attribution, not an auth boundary
    AuditToolCallRequest:
      type: object
      required:
        - tool_name
      properties:
        tool_name:
          type: string
          description: Name of the tool that was called
        caller_name:
          type: string
          description: >-
            Which client/integration made this call (e.g. claude_code, codex,
            cursor, openclaw). Replaces tool_type (#2912), which was misnamed
            for this purpose — every real caller used it to identify itself, not
            to describe a property of the tool.
        tool_type:
          type: string
          deprecated: true
          description: >-
            Deprecated — use caller_name instead. Accepted as a legacy input
            fallback when caller_name is not supplied.
        input:
          type: object
          description: Input data sent to the tool
        output:
          type: object
          description: Output data returned by the tool
        workflow_id:
          type: string
          description: Associated workflow ID
        step_id:
          type: string
          description: Associated workflow step ID
        user_id:
          type: string
          description: User who triggered the tool call
        duration_ms:
          type: integer
          format: int64
          description: Duration of the tool call in milliseconds
        policies_applied:
          type: array
          items:
            type: string
          description: List of policy names applied during the tool call
        success:
          type: boolean
          description: Whether the tool call succeeded
        error_message:
          type: string
          description: Error message if the tool call failed
    AuditToolCallResponse:
      type: object
      properties:
        audit_id:
          type: string
          description: Unique identifier for the audit entry
        status:
          type: string
          description: Recording status
          enum:
            - recorded
        timestamp:
          type: string
          format: date-time
          description: When the audit entry was recorded
    WorkflowExecuteRequest:
      type: object
      required:
        - workflow
      properties:
        workflow:
          $ref: '#/components/schemas/Workflow'
        input:
          type: object
          additionalProperties: true
        user:
          $ref: '#/components/schemas/UserContext'
    Workflow:
      type: object
      required:
        - metadata
        - spec
      properties:
        metadata:
          type: object
          required:
            - name
          properties:
            name:
              type: string
            description:
              type: string
        spec:
          type: object
          required:
            - steps
          properties:
            steps:
              type: array
              items:
                $ref: '#/components/schemas/WorkflowStep'
    WorkflowStep:
      type: object
      required:
        - name
        - type
      properties:
        name:
          type: string
        type:
          type: string
          enum:
            - mcp_query
            - llm
            - api_call
            - transform
        connector:
          type: string
        query:
          type: string
        prompt:
          type: string
        dependencies:
          type: array
          items:
            type: string
    WorkflowExecution:
      type: object
      properties:
        id:
          type: string
        workflow_name:
          type: string
        status:
          type: string
          enum:
            - pending
            - running
            - completed
            - failed
        started_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
        steps:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              status:
                type: string
              process_time:
                type: string
        output:
          type: object
          additionalProperties: true
    CreateWorkflowRequest:
      type: object
      required:
        - workflow_name
      properties:
        workflow_name:
          type: string
          description: Human-readable name for the workflow
          example: code-review-pipeline
        source:
          type: string
          description: Source orchestrator
          enum:
            - langgraph
            - langchain
            - crewai
            - external
          default: external
        trace_id:
          type: string
          maxLength: 255
          description: >-
            External trace ID for correlation with Langsmith, Datadog, or
            OpenTelemetry
          example: langsmith-trace-abc123
        metadata:
          type: object
          description: Additional workflow metadata
          additionalProperties: true
    CreateWorkflowResponse:
      type: object
      properties:
        workflow_id:
          type: string
          description: Unique workflow identifier
          example: wf_abc123
        workflow_name:
          type: string
        status:
          type: string
          enum:
            - in_progress
            - completed
            - aborted
            - failed
        trace_id:
          type: string
          description: >-
            External trace ID for correlation with Langsmith, Datadog, or
            OpenTelemetry
          example: langsmith-trace-abc123
        started_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
          description: |
            When the workflow row was inserted. Distinct from
            `started_at` because workflows can be created in a
            staged-but-not-running state.
        source:
          type: string
          enum:
            - langgraph
            - langchain
            - crewai
            - external
          description: |
            Echoes the request's `source` so callers don't need a
            separate read after creation to learn which orchestrator
            owns the workflow.
    WorkflowStatusResponse:
      type: object
      properties:
        workflow_id:
          type: string
        workflow_name:
          type: string
        source:
          type: string
          enum:
            - langgraph
            - langchain
            - crewai
            - external
        status:
          type: string
          enum:
            - in_progress
            - completed
            - aborted
            - failed
        trace_id:
          type: string
          description: >-
            External trace ID for correlation with Langsmith, Datadog, or
            OpenTelemetry
        current_step_index:
          type: integer
        total_steps:
          type: integer
        started_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
        metadata:
          type: object
          additionalProperties: true
        steps:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowStepInfo'
    WorkflowStepInfo:
      type: object
      properties:
        step_id:
          type: string
        step_index:
          type: integer
        step_name:
          type: string
        step_type:
          type: string
          enum:
            - llm_call
            - tool_call
            - connector_call
            - human_task
        decision:
          type: string
          enum:
            - allow
            - block
            - require_approval
        approval_status:
          type: string
          enum:
            - pending
            - approved
            - rejected
            - expired
        gate_checked_at:
          type: string
          format: date-time
        approved_by:
          type: string
          description: |
            User email of the approver when `approval_status` is
            `approved`. Empty for `pending`, and empty for `expired`
            (auto-timeout — no human reviewer) or for `rejected` paths
            without a recorded reviewer.
        completed_at:
          type: string
          format: date-time
          description: |
            When the step transitioned to a terminal state (approved,
            rejected, or auto-completed). Pair with `gate_checked_at`
            to compute approval latency.
        decision_reason:
          type: string
          description: |
            Free-form rationale recorded alongside the decision —
            populated by the approver when present, or by the policy
            engine for auto-decisions. Per-step audit context.
    ToolContext:
      type: object
      description: Tool-level context for per-tool governance within tool_call steps.
      required:
        - tool_name
      properties:
        tool_name:
          type: string
          description: Name of the tool being invoked
          example: web_search
        tool_type:
          type: string
          description: 'Tool type: function, mcp, or api'
          enum:
            - function
            - mcp
            - api
          example: function
        tool_input:
          type: object
          description: Tool input parameters
          additionalProperties: true
    StepGateRequest:
      type: object
      required:
        - step_type
      properties:
        step_name:
          type: string
          description: Human-readable step name (optional)
          example: Generate Code
        step_type:
          type: string
          description: Type of step
          enum:
            - llm_call
            - tool_call
            - connector_call
            - human_task
          example: llm_call
        step_input:
          type: object
          description: Input data for the step (for policy evaluation)
          additionalProperties: true
        model:
          type: string
          description: LLM model being used
          example: gpt-4
        provider:
          type: string
          description: LLM provider being used
          example: openai
        tokens_in:
          type: integer
          description: Estimated input tokens for the step (used at gate time)
          example: 150
        tokens_out:
          type: integer
          description: Estimated output tokens for the step (used at gate time)
          example: 45
        cost_usd:
          type: number
          format: double
          description: Estimated cost in USD for the step (used at gate time)
          example: 0.0023
        tool_context:
          $ref: '#/components/schemas/ToolContext'
        retry_policy:
          type: string
          description: >
            Controls behavior on repeated calls for the same (workflow_id,
            step_id).

            Default ("idempotent"): return cached decision from prior
            evaluation.

            "reevaluate": force fresh policy evaluation regardless of prior
            decision.
          enum:
            - idempotent
            - reevaluate
          default: idempotent
          example: idempotent
        idempotency_key:
          type: string
          maxLength: 255
          description: >
            Optional caller-supplied opaque business-level key (Issue #1673
            Phase 2).

            Recorded on the first /gate call that sets it; immutable for the
            step's

            lifetime. Subsequent /gate and /complete calls MUST pass the same
            key

            or receive 409 IDEMPOTENCY_KEY_MISMATCH. Use business-meaningful
            values

            like `payment:wire:invoice-7721`, not request IDs.
          example: payment:wire:invoice-7721
    RetryContext:
      type: object
      description: |
        First-class retry and execution state (Issue #1673 Phase 1). Always
        present on every `StepGateResponse`, including the first gate call.
        Replaces the ambiguous `cached: bool` signal with unambiguous state
        the agent and policy engine can reason about.
      required:
        - gate_count
        - completion_count
        - prior_completion_status
        - prior_output_available
        - prior_output
        - prior_completion_at
        - first_attempt_at
        - last_attempt_at
        - last_decision
        - idempotency_key
      properties:
        gate_count:
          type: integer
          minimum: 1
          description: |
            Number of /gate calls for this (workflow_id, step_id), including
            the current call. First call returns 1.
          example: 2
        completion_count:
          type: integer
          minimum: 0
          description: |
            Number of /complete calls for this (workflow_id, step_id). Normally
            0 on first gate, 1 after the step completes.
          example: 1
        prior_completion_status:
          type: string
          enum:
            - none
            - completed
            - gated_not_completed
          description: >
            "none" on first gate call. "completed" when a prior /gate +
            /complete

            both landed. "gated_not_completed" when a prior /gate landed but no

            /complete followed — uncertain territory the agent needs to
            reconcile

            against the downstream system before re-executing.
          example: completed
        prior_output_available:
          type: boolean
          description: |
            True iff prior_completion_status == "completed". Mirrors whether
            prior_output *could* be returned if include_prior_output=true.
          example: true
        prior_output:
          type: object
          nullable: true
          additionalProperties: true
          description: |
            Always present in the schema. Populated only when the caller set
            ?include_prior_output=true AND prior_output_available is true.
            Otherwise null.
        prior_completion_at:
          type: string
          format: date-time
          nullable: true
          description: Timestamp of the prior /complete call, if any.
        first_attempt_at:
          type: string
          format: date-time
          description: |
            Timestamp of the first /gate call for this step. On the first call,
            equals last_attempt_at.
        last_attempt_at:
          type: string
          format: date-time
          description: Timestamp of this /gate call.
        last_decision:
          type: string
          enum:
            - allow
            - block
            - require_approval
          description: |
            Decision of the immediately prior /gate call. On the first call
            (gate_count == 1), equals the current decision (first-call
            invariant).
          example: allow
        idempotency_key:
          type: string
          description: |
            The caller-supplied business-level key recorded on this step
            (Issue #1673 Phase 2). Always present in the schema — empty
            string `""` if the caller never supplied one.
          example: payment:wire:invoice-7721
    StepGateResponse:
      type: object
      properties:
        decision:
          type: string
          description: Gate decision
          enum:
            - allow
            - block
            - require_approval
        step_id:
          type: string
          description: Step identifier
        decision_id:
          type: string
          description: Unique decision identifier for auditing
        reason:
          type: string
          description: Reason for block or approval requirement
        policy_ids:
          type: array
          description: IDs of policies that matched
          items:
            type: string
        approval_url:
          type: string
          description: URL for human approval (Enterprise)
          format: uri
        policies_evaluated:
          type: array
          description: All policies that were checked during evaluation (Issue
          items:
            $ref: '#/components/schemas/PolicyMatch'
        policies_matched:
          type: array
          description: Policies that matched and contributed to the decision (Issue
          items:
            $ref: '#/components/schemas/PolicyMatch'
        cached:
          type: boolean
          deprecated: true
          description: |
            **Deprecated (Issue #1673).** Whether this response was served from
            a prior decision rather than a fresh policy evaluation. Use
            `retry_context.gate_count > 1` instead — `cached` conflates
            first-call-no vs many-retries-yes into a single bit. Kept
            populated on every response for back-compat; removal planned
            for a future major version.
          example: false
        decision_source:
          type: string
          deprecated: true
          description: |
            **Deprecated (Issue #1673).** "fresh" or "cached". Use
            `retry_context.prior_completion_status` for the distinction
            agents and policies actually need. Kept populated on every
            response for back-compat; removal planned for a future major.
          enum:
            - fresh
            - cached
          example: fresh
        retry_context:
          $ref: '#/components/schemas/RetryContext'
    MarkStepCompletedRequest:
      type: object
      properties:
        output:
          type: object
          description: Output data from the step
          additionalProperties: true
        tokens_in:
          type: integer
          description: >-
            Actual input tokens consumed by the step (overrides gate-time
            estimate)
          example: 150
        tokens_out:
          type: integer
          description: >-
            Actual output tokens produced by the step (overrides gate-time
            estimate)
          example: 45
        cost_usd:
          type: number
          format: double
          description: Actual cost in USD for the step (overrides gate-time estimate)
          example: 0.0023
        idempotency_key:
          type: string
          maxLength: 255
          description: |
            Optional caller-supplied key (Issue #1673 Phase 2). Must match the
            key recorded on the step's earlier /gate call. Mismatch returns
            409 IDEMPOTENCY_KEY_MISMATCH.
          example: payment:wire:invoice-7721
        metadata:
          type: object
          additionalProperties: true
          description: |
            Free-form metadata captured at step-completion time —
            useful for audit context the gate-time data didn't have
            (post-execution latency from a downstream service, retry
            attempt counters, etc.). Treat as opaque on the client.
    APIErrorDetails:
      type: object
      description: Structured error details used by typed SDK exceptions.
      properties:
        workflow_id:
          type: string
        step_id:
          type: string
        expected_idempotency_key:
          type: string
          description: |
            The key recorded on the step's /gate call. Empty string when the
            gate call had no key but the /complete call supplied one.
        received_idempotency_key:
          type: string
          description: |
            The key the caller just passed. Empty string when the /complete
            call omitted a key that the /gate call had set.
    APIError:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: Machine-readable error code (e.g. IDEMPOTENCY_KEY_MISMATCH).
        message:
          type: string
          description: Human-readable error description.
        details:
          $ref: '#/components/schemas/APIErrorDetails'
    APIErrorResponse:
      type: object
      description: Structured error response envelope used by WCP endpoints.
      required:
        - error
      properties:
        error:
          $ref: '#/components/schemas/APIError'
    Checkpoint:
      type: object
      properties:
        id:
          type: integer
          format: int64
          description: Database identifier
        workflow_id:
          type: string
        step_id:
          type: string
        step_index:
          type: integer
        step_type:
          type: string
          description: Type of step (llm_call, tool_call, etc.)
        checkpoint_type:
          type: string
          enum:
            - step_gate
            - approval_boundary
          description: step_gate for standard gates, approval_boundary for require_approval
        gate_decision:
          type: string
          enum:
            - allow
            - block
            - require_approval
        gate_reason:
          type: string
        is_resumable:
          type: boolean
          description: False for blocked steps (no point resuming from a hard block)
        resume_count:
          type: integer
          description: How many times the workflow has been resumed from this checkpoint
        created_at:
          type: string
          format: date-time
    CheckpointListResponse:
      type: object
      properties:
        checkpoints:
          type: array
          items:
            $ref: '#/components/schemas/Checkpoint'
        workflow_id:
          type: string
    ResumeFromCheckpointResponse:
      type: object
      properties:
        workflow_id:
          type: string
        resumed_from_checkpoint:
          type: string
          description: step_id of the checkpoint
        resumed_from_index:
          type: integer
        new_decision:
          type: string
          enum:
            - allow
            - block
            - require_approval
        decision_source:
          type: string
          description: Always "fresh" since resume forces re-evaluation
        resume_count:
          type: integer
        message:
          type: string
    ListWorkflowsResponse:
      type: object
      properties:
        workflows:
          type: array
          items:
            $ref: '#/components/schemas/WorkflowStatusResponse'
        total:
          type: integer
          description: Total number of workflows matching filters
        limit:
          type: integer
        offset:
          type: integer
    ApprovalResponse:
      type: object
      description: |
        Rich response returned by the WCP `/approve` and `/reject` endpoints
        and by the MAP plan-scoped equivalents
        (`/api/v1/plans/{id}/steps/{step_id}/approve|reject`). Both planes
        project through the same helper — see ADR-046 (HITL response parity)
        and ADR-045 (retry_context wire contract).

        `decision` resolves to `allow` on a successful approval (the step can
        now proceed) or `block` on rejection (workflow aborted). `plan_id` is
        populated only on MAP-plane responses; on WCP-plane responses it is
        omitted. `retry_context` is always present and mirrors the StepGate
        `retry_context` shape.
      properties:
        workflow_id:
          type: string
          description: Underlying WCP workflow identifier
          example: wf_abc123
        plan_id:
          type: string
          description: |
            MAP plan id — present on MAP-plane responses. Omitted on WCP-plane
            responses (WCP has no plan concept).
          example: plan-42
        step_id:
          type: string
          description: Step that was approved or rejected
          example: step-2
        status:
          type: string
          enum:
            - pending
            - approved
            - rejected
            - expired
          description: |
            Flat string alias of `approval_status`. Both fields always carry
            the same value — `status` is convenient for loggers, dashboards,
            and clients that prefer a simple string; `approval_status` is the
            typed source of truth. First-class on both the WCP and MAP
            response shapes so existing clients reading either field keep
            working without branching.
          example: approved
        decision:
          type: string
          enum:
            - allow
            - block
            - require_approval
          description: |
            Post-approval decision. Approved `require_approval` steps resolve
            to `allow`; rejected to `block`. `require_approval` on an
            approve/reject response means the step is still pending.
          example: allow
        reason:
          type: string
          description: |
            Decision reason text. Approved / rejected responses prefix the
            original policy reason with `Approved:` or `Rejected:`.
          example: 'Approved: High-value transfer requires oversight'
        approval_status:
          type: string
          enum:
            - pending
            - approved
            - rejected
            - expired
          description: |
            Terminal approval status after the mutation landed. `expired` is an
            auto-timeout (Evaluation-tier) — a terminal not-approved state that
            blocks the step, kept distinct from a human `rejected`.
          example: approved
        approval_id:
          type: string
          format: uuid
          description: |
            Deterministic HITL queue entry UUID (UUID v5 over
            `(workflow_id, step_id)`). Matches the queue row written by the
            WCP HITL adapter. Empty on the legacy in-memory MAP flow when no
            queue row exists.
          example: 318a270f-7b42-5c56-a191-8dbd1bf2e1e4
        approved_by:
          type: string
          description: Identity (X-User-ID, typically email) that approved the step
          example: fraud.analyst@banking.example
        approved_at:
          type: string
          format: date-time
          description: Timestamp when the approval was persisted
        rejected_by:
          type: string
          description: Identity that rejected the step (rejection path only)
          example: fraud.analyst@banking.example
        rejected_at:
          type: string
          format: date-time
          description: Timestamp when the rejection was persisted
        policies_matched:
          type: array
          description: Policies that triggered the original `require_approval` decision
          items:
            $ref: '#/components/schemas/PolicyMatch'
        retry_context:
          $ref: '#/components/schemas/RetryContext'
        message:
          type: string
          description: Human-readable status summary
          example: Step approved
    PendingApproval:
      type: object
      description: |
        A workflow step awaiting human approval. Returned by both
        `/api/v1/workflows/approvals/pending` (WCP plane) and
        `/api/v1/plans/approvals/pending` (MAP plane). The `plan_id` field is
        the one intentional asymmetry between the two planes — populated on
        MAP-plane responses, omitted on WCP-plane responses.
      properties:
        workflow_id:
          type: string
          description: WCP workflow identifier.
          example: wf_abc123
        workflow_name:
          type: string
          description: Human-readable workflow name.
          example: code-review-pipeline
        plan_id:
          type: string
          description: |
            MAP plan identifier. Populated on the MAP-plane listing
            (`/api/v1/plans/approvals/pending`); omitted (omitempty) on the
            WCP-plane listing.
          example: plan-abc123
        step_id:
          type: string
          description: Step awaiting approval.
          example: step-2
        step_index:
          type: integer
          description: Zero-based index of the step within the workflow.
          example: 1
        step_name:
          type: string
          description: Human-readable step name.
          example: Deploy to Production
        step_type:
          type: string
          enum:
            - llm_call
            - tool_call
            - connector_call
            - human_task
            - synthesis
            - action
            - gate
          description: Type of step operation.
          example: action
        decision:
          type: string
          enum:
            - allow
            - block
            - require_approval
          description: >-
            Gate decision that paused the step — always `require_approval` for
            pending entries.
          example: require_approval
        decision_reason:
          type: string
          description: Why approval is required.
          example: Human approval required for deployment steps
        policies_matched:
          type: array
          description: Policies that triggered the approval requirement.
          items:
            type: object
        step_input:
          type: object
          description: Step input payload (may be redacted by PII rules).
        approval_status:
          type: string
          enum:
            - pending
            - approved
            - rejected
          description: Current approval state — `pending` for listed entries.
          example: pending
        created_at:
          type: string
          format: date-time
          description: Time of the first /gate call that paused the step.
    PendingApprovalsResponse:
      type: object
      description: List of pending approvals across workflows for the caller's tenant.
      properties:
        pending_approvals:
          type: array
          items:
            $ref: '#/components/schemas/PendingApproval'
        count:
          type: integer
          description: Total number of pending approvals matching the scope.
    CreateWebhookRequest:
      type: object
      required:
        - url
        - events
      properties:
        url:
          type: string
          format: uri
          description: URL to receive webhook event payloads
          example: https://example.com/webhooks/axonflow
        events:
          type: array
          items:
            type: string
          description: >
            List of event types to subscribe to. Available events:

            - `policy.violation` — Policy violation detected

            - `policy.created` / `policy.updated` / `policy.deleted` — Policy
            lifecycle

            - `workflow.completed` / `workflow.failed` / `workflow.aborted` —
            Workflow lifecycle

            - `workflow.approval_required` — Step requires human approval

            - `budget.threshold_reached` / `budget.exceeded` / `budget.blocked`
            — Budget alerts

            - `plan.completed` / `plan.failed` — Plan lifecycle
          example:
            - policy.violation
            - workflow.completed
        secret:
          type: string
          description: >-
            Secret key for HMAC-SHA256 signature verification of webhook
            payloads
          example: whsec_example_placeholder
        active:
          type: boolean
          default: true
          description: Whether the subscription is active
    UpdateWebhookRequest:
      type: object
      properties:
        url:
          type: string
          format: uri
          description: Updated URL to receive webhook event payloads
        events:
          type: array
          items:
            type: string
          description: Updated list of event types to subscribe to
        active:
          type: boolean
          description: Whether the subscription is active
    WebhookSubscription:
      type: object
      description: A webhook subscription for receiving event notifications
      properties:
        id:
          type: string
          description: Unique subscription identifier
          example: wh_abc123
        url:
          type: string
          format: uri
          description: URL that receives webhook event payloads
          example: https://example.com/webhooks/axonflow
        events:
          type: array
          items:
            type: string
          description: Event types this subscription listens for
          example:
            - policy.violation
            - workflow.completed
        active:
          type: boolean
          description: Whether the subscription is currently active
          example: true
        tenant_id:
          type: string
          description: Tenant ID that owns this subscription
          example: tenant-1
        org_id:
          type: string
          description: Organization ID that owns this subscription
          example: org-1
        secret:
          type: string
          description: Secret key for HMAC-SHA256 signature verification
          example: whsec_example_placeholder
        created_at:
          type: string
          format: date-time
          description: When the subscription was created
        updated_at:
          type: string
          format: date-time
          description: When the subscription was last updated
    ListWebhookSubscriptionsResponse:
      type: object
      description: List of webhook subscriptions
      properties:
        subscriptions:
          type: array
          items:
            $ref: '#/components/schemas/WebhookSubscription'
        total:
          type: integer
          description: Total number of subscriptions
    ConnectorInfo:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        type:
          type: string
        description:
          type: string
        version:
          type: string
        capabilities:
          type: array
          items:
            type: string
        installed:
          type: boolean
        healthy:
          type: boolean
    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: string
    AgentResource:
      type: object
      properties:
        id:
          type: string
          description: Qualified ID (domain/name)
          example: travel/flight-booking
        name:
          type: string
          description: Agent name
          example: flight-booking
        domain:
          type: string
          description: Agent domain
          example: travel
        description:
          type: string
          description: Agent description
        version:
          type: integer
          description: Version number
        is_active:
          type: boolean
          description: Whether agent is active
        config:
          $ref: '#/components/schemas/AgentConfigSpec'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    AgentConfigSpec:
      type: object
      description: Agent configuration specification
      properties:
        execution:
          type: object
          properties:
            default_mode:
              type: string
              enum:
                - auto
                - parallel
                - sequential
            max_parallel_tasks:
              type: integer
            timeout_seconds:
              type: integer
        agents:
          type: array
          items:
            $ref: '#/components/schemas/AgentDefinition'
        routing:
          type: array
          items:
            $ref: '#/components/schemas/RoutingRule'
    AgentDefinition:
      type: object
      required:
        - name
        - type
      properties:
        name:
          type: string
        type:
          type: string
          enum:
            - llm-call
            - connector-call
        llm:
          type: object
          description: LLM config (required for llm-call type)
          properties:
            provider:
              type: string
            model:
              type: string
        connector:
          type: object
          description: Connector config (required for connector-call type)
          properties:
            name:
              type: string
            operation:
              type: string
    RoutingRule:
      type: object
      required:
        - pattern
        - agent
      properties:
        pattern:
          type: string
          description: Regex pattern to match
        agent:
          type: string
          description: Target agent name
        priority:
          type: integer
          description: Higher priority rules match first
    AgentListResponse:
      type: object
      properties:
        agents:
          type: array
          items:
            $ref: '#/components/schemas/AgentResource'
        pagination:
          type: object
          properties:
            page:
              type: integer
            page_size:
              type: integer
            total:
              type: integer
            total_pages:
              type: integer
    AgentResponse:
      type: object
      properties:
        agent:
          $ref: '#/components/schemas/AgentResource'
    CreateAgentRequest:
      type: object
      required:
        - name
        - config
      properties:
        name:
          type: string
          description: Agent name (lowercase, alphanumeric, hyphens, underscores)
        domain:
          type: string
          description: Agent domain
        description:
          type: string
        is_active:
          type: boolean
          default: true
        config:
          $ref: '#/components/schemas/AgentConfigSpec'
    UpdateAgentRequest:
      type: object
      properties:
        name:
          type: string
        domain:
          type: string
        description:
          type: string
        is_active:
          type: boolean
        config:
          $ref: '#/components/schemas/AgentConfigSpec'
    ValidateAgentRequest:
      type: object
      required:
        - config
      properties:
        name:
          type: string
        domain:
          type: string
        config:
          $ref: '#/components/schemas/AgentConfigSpec'
    ValidationResponse:
      type: object
      properties:
        valid:
          type: boolean
        errors:
          type: array
          items:
            type: string
    TestAgentRequest:
      type: object
      required:
        - query
      properties:
        query:
          type: string
          description: Test query to execute
        context:
          type: object
          additionalProperties: true
          description: Additional context for the query
    TestAgentResponse:
      type: object
      properties:
        success:
          type: boolean
        result:
          description: Test execution result
        execution_time_ms:
          type: integer
        tasks_executed:
          type: integer
        errors:
          type: array
          items:
            type: string
    AgentVersionsResponse:
      type: object
      properties:
        versions:
          type: array
          items:
            type: object
            properties:
              version:
                type: integer
              change_type:
                type: string
                enum:
                  - create
                  - update
                  - delete
                  - activate
                  - deactivate
              changed_at:
                type: string
                format: date-time
              changed_by:
                type: string
                format: uuid
              change_summary:
                type: string
    SEBIComplianceFramework:
      type: string
      description: SEBI compliance framework identifier
      enum:
        - SEBI_AI_ML
        - DPDP_ACT_2023
        - SEBI_DPDP_COMBINED
    SEBIExportFormat:
      type: string
      description: Export output format
      enum:
        - json
        - csv
        - xml
    SEBIAuditDataType:
      type: string
      description: Type of audit data
      enum:
        - policy_violations
        - llm_calls
        - decision_chain
        - hitl_oversight
        - pii_redactions
        - all
    SEBIComplianceStatus:
      type: string
      description: Compliance status indicator
      enum:
        - COMPLIANT
        - NON_COMPLIANT
        - WARNING
    SEBIDashboard:
      type: object
      description: SEBI compliance dashboard data
      properties:
        framework:
          $ref: '#/components/schemas/SEBIComplianceFramework'
        overall_score:
          type: integer
          minimum: 0
          maximum: 100
          description: Overall compliance score (0-100)
        overall_status:
          $ref: '#/components/schemas/SEBIComplianceStatus'
        last_audit_export:
          type: string
          format: date-time
          description: Timestamp of last audit export
        retention_status:
          $ref: '#/components/schemas/SEBIRetentionResponse'
        readiness:
          $ref: '#/components/schemas/SEBIComplianceReadiness'
        violations_summary:
          $ref: '#/components/schemas/SEBIViolationsSummary'
        pii_summary:
          $ref: '#/components/schemas/SEBIPIISummary'
        hitl_reviews_pending:
          type: integer
          description: Number of HITL reviews awaiting action
        last_updated:
          type: string
          format: date-time
    SEBIAuditExportRequest:
      type: object
      description: Request to export SEBI audit data
      required:
        - start_date
        - end_date
      properties:
        start_date:
          type: string
          format: date-time
          description: Start of export period (inclusive)
        end_date:
          type: string
          format: date-time
          description: End of export period (inclusive)
        data_types:
          type: array
          items:
            $ref: '#/components/schemas/SEBIAuditDataType'
          description: Types of audit data to export (defaults to all)
        format:
          $ref: '#/components/schemas/SEBIExportFormat'
        framework:
          $ref: '#/components/schemas/SEBIComplianceFramework'
        include_archived:
          type: boolean
          default: false
          description: Include records from cold storage
        redact_pii:
          type: boolean
          default: false
          description: Redact PII in export (for external auditors)
        filters:
          $ref: '#/components/schemas/SEBIAuditExportFilters'
    SEBIAuditExportFilters:
      type: object
      description: Optional filters for audit exports
      properties:
        agent_ids:
          type: array
          items:
            type: string
          description: Filter by agent IDs
        user_ids:
          type: array
          items:
            type: integer
          description: Filter by user IDs
        severity:
          type: string
          description: Minimum severity level
        policy_types:
          type: array
          items:
            type: string
          description: Filter by policy types
        violation_types:
          type: array
          items:
            type: string
          description: Filter by violation types
        include_model_info:
          type: boolean
          description: Include detailed model information
    SEBIAuditExportResponse:
      type: object
      description: Response for audit export requests
      properties:
        export_id:
          type: string
          description: Unique export identifier
        status:
          type: string
          enum:
            - pending
            - processing
            - completed
            - failed
          description: Export status
        exported_at:
          type: string
          format: date-time
          description: When the export was completed
        framework:
          $ref: '#/components/schemas/SEBIComplianceFramework'
        summary:
          $ref: '#/components/schemas/SEBIAuditExportSummary'
        download_url:
          type: string
          description: Presigned URL to download the export from cloud storage
        expires_at:
          type: string
          format: date-time
          description: When the download URL expires
        storage_type:
          type: string
          enum:
            - local
            - s3
            - gcs
            - azure
          description: Storage backend used for this export
        storage_key:
          type: string
          description: Cloud storage object key (path within bucket/container)
        file_size_bytes:
          type: integer
          description: Size of the export file in bytes
        file_checksum:
          type: string
          description: SHA-256 checksum of the export file
        metadata:
          $ref: '#/components/schemas/SEBIExportMetadata'
    SEBIAuditExportSummary:
      type: object
      description: Statistics about the export
      properties:
        total_records:
          type: integer
          description: Total records exported
        records_by_type:
          type: object
          additionalProperties:
            type: integer
          description: Records by data type
        date_range:
          type: object
          properties:
            start:
              type: string
              format: date-time
            end:
              type: string
              format: date-time
        violations_summary:
          $ref: '#/components/schemas/SEBIViolationsSummary'
        compliance_score:
          type: number
          format: float
          description: Compliance score for the period
    SEBIExportMetadata:
      type: object
      description: Export metadata for audit trail
      properties:
        export_version:
          type: string
          description: Schema version
        generated_by:
          type: string
          description: System that generated the export
        generated_at:
          type: string
          format: date-time
        org_id:
          type: integer
        org_name:
          type: string
        requested_by:
          type: string
          description: User who requested the export
        compliance_framework:
          $ref: '#/components/schemas/SEBIComplianceFramework'
        retention_days:
          type: integer
          description: Retention period (1825 days = 5 years)
        checksum:
          type: string
          description: SHA-256 checksum of export data
        signed_by:
          type: string
          description: Signing authority (for signed exports)
    RBIAuditExportRequest:
      type: object
      description: Request to create an RBI audit export
      required:
        - export_type
        - format
      properties:
        export_type:
          type: string
          enum:
            - full
            - incremental
          description: Type of audit export
        format:
          type: string
          enum:
            - json
            - csv
            - xml
          description: Export file format
        start_date:
          type: string
          format: date-time
          description: Start of date range (for incremental exports)
        end_date:
          type: string
          format: date-time
          description: End of date range (for incremental exports)
        requested_by:
          type: string
          description: User or service requesting the export
        purpose:
          type: string
          description: Purpose of the export (for audit trail)
    RBIAuditExport:
      type: object
      description: RBI audit export record
      properties:
        id:
          type: string
          description: Unique export identifier
        org_id:
          type: string
        status:
          type: string
          enum:
            - pending
            - processing
            - completed
            - failed
          description: Export status
        export_type:
          type: string
          enum:
            - full
            - incremental
        format:
          type: string
          enum:
            - json
            - csv
            - xml
        requested_by:
          type: string
        purpose:
          type: string
        created_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
          nullable: true
        download_url:
          type: string
          description: Presigned URL for cloud storage downloads
          nullable: true
        storage_type:
          type: string
          enum:
            - local
            - s3
            - gcs
            - azure
          description: Storage backend used for this export
        storage_key:
          type: string
          description: Cloud storage object key
          nullable: true
        file_path:
          type: string
          description: Local file path (when storage_type is local)
          nullable: true
        file_size_bytes:
          type: integer
          description: Size of export file in bytes
        file_checksum:
          type: string
          description: SHA-256 checksum of the export file
        record_count:
          type: integer
          description: Number of records in the export
        error_message:
          type: string
          nullable: true
          description: Error details if export failed
    EUAIActExportRequest:
      type: object
      description: Request to create an EU AI Act compliance export
      required:
        - export_type
        - format
      properties:
        export_type:
          type: string
          enum:
            - full_audit
            - conformity_evidence
            - hitl_summary
            - decision_chain
            - policy_violations
            - accuracy_metrics
          description: Type of compliance export
        format:
          type: string
          enum:
            - json
            - xml
            - csv
          description: Export file format
        date_from:
          type: string
          format: date-time
          description: Start of date range
        date_to:
          type: string
          format: date-time
          description: End of date range
        model_ids:
          type: array
          items:
            type: string
          description: Filter by specific model IDs (optional)
    EUAIActExport:
      type: object
      description: EU AI Act compliance export record
      properties:
        id:
          type: string
          description: Unique export identifier
        org_id:
          type: string
        status:
          type: string
          enum:
            - pending
            - processing
            - completed
            - failed
          description: Export processing status
        export_type:
          type: string
          enum:
            - full_audit
            - conformity_evidence
            - hitl_summary
            - decision_chain
            - policy_violations
            - accuracy_metrics
        format:
          type: string
          enum:
            - json
            - xml
            - csv
        created_at:
          type: string
          format: date-time
        started_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
        date_from:
          type: string
          format: date-time
        date_to:
          type: string
          format: date-time
        download_url:
          type: string
          description: Presigned URL for cloud storage downloads
          nullable: true
        storage_type:
          type: string
          enum:
            - local
            - s3
            - gcs
            - azure
          description: Storage backend used for this export
        storage_key:
          type: string
          description: Cloud storage object key
          nullable: true
        file_path:
          type: string
          description: Local file path (when storage_type is local)
          nullable: true
        file_size:
          type: integer
          description: Size of export file in bytes
        record_count:
          type: integer
          description: Number of records in the export
        progress:
          type: number
          format: float
          description: Processing progress (0.0 to 1.0)
        error_message:
          type: string
          nullable: true
          description: Error details if export failed
        created_by:
          type: string
    SEBIRetentionResponse:
      type: object
      description: 5-year retention compliance status
      properties:
        org_id:
          type: integer
        framework:
          $ref: '#/components/schemas/SEBIComplianceFramework'
        compliance_status:
          $ref: '#/components/schemas/SEBIComplianceStatus'
        status:
          type: array
          items:
            $ref: '#/components/schemas/SEBIDataTypeRetentionStatus'
        next_cleanup:
          type: string
          format: date-time
          description: Next scheduled cleanup
    SEBIDataTypeRetentionStatus:
      type: object
      description: Retention status for a data type
      properties:
        data_type:
          $ref: '#/components/schemas/SEBIAuditDataType'
        retention_days:
          type: integer
          description: Configured retention period
        oldest_record:
          type: string
          format: date-time
        newest_record:
          type: string
          format: date-time
        total_records:
          type: integer
          format: int64
        archived_records:
          type: integer
          format: int64
        storage_bytes:
          type: integer
          format: int64
        compliance_status:
          $ref: '#/components/schemas/SEBIComplianceStatus'
        last_cleanup:
          type: string
          format: date-time
    SEBIComplianceReadiness:
      type: object
      description: SEBI audit readiness assessment
      properties:
        ready:
          type: boolean
          description: Organization is ready for audit
        score:
          type: integer
          minimum: 0
          maximum: 100
          description: Readiness score (0-100)
        checks:
          type: array
          items:
            $ref: '#/components/schemas/SEBIComplianceCheck'
        recommendations:
          type: array
          items:
            type: string
          description: Improvement recommendations
    SEBIComplianceCheck:
      type: object
      description: Individual compliance check result
      properties:
        name:
          type: string
          description: Check name
        description:
          type: string
          description: What the check verifies
        status:
          type: string
          enum:
            - pass
            - fail
            - warning
        details:
          type: string
          description: Additional information
        message:
          type: string
          description: Status message (null if pass)
    SEBIViolationsSummary:
      type: object
      description: Policy violations summary
      properties:
        total:
          type: integer
          description: Total violations
        by_severity:
          type: object
          properties:
            critical:
              type: integer
            high:
              type: integer
            medium:
              type: integer
            low:
              type: integer
        by_type:
          type: object
          additionalProperties:
            type: integer
          description: Violations by type
        trend:
          type: string
          enum:
            - improving
            - stable
            - degrading
          description: Trend direction
    SEBIPIISummary:
      type: object
      description: PII detection and redaction metrics
      properties:
        total_detections:
          type: integer
          description: Total PII detections
        total_redactions:
          type: integer
          description: Total PII redactions
        by_type:
          type: object
          properties:
            pan:
              type: integer
              description: Indian PAN card detections
            aadhaar:
              type: integer
              description: Indian Aadhaar card detections
            email:
              type: integer
            phone:
              type: integer
            other:
              type: integer
        redaction_rate_percent:
          type: number
          format: float
          description: Percentage of detections that were redacted
    LLMProviderResource:
      type: object
      description: LLM provider configuration
      properties:
        name:
          type: string
          description: Unique provider name
        type:
          type: string
          enum:
            - openai
            - azure-openai
            - anthropic
            - bedrock
            - ollama
            - gemini
            - custom
          description: Provider type
        endpoint:
          type: string
          description: API endpoint URL
        model:
          type: string
          description: Default model name
        region:
          type: string
          description: AWS region (for Bedrock)
        enabled:
          type: boolean
          description: Whether provider is enabled
        priority:
          type: integer
          description: Routing priority (lower = higher priority)
        weight:
          type: integer
          description: Routing weight for load balancing
        rate_limit:
          type: integer
          description: Max requests per second
        timeout_seconds:
          type: integer
          description: Request timeout in seconds
        has_api_key:
          type: boolean
          description: Whether API key is configured (key not exposed)
        settings:
          type: object
          additionalProperties: true
          description: Provider-specific settings
        health:
          $ref: '#/components/schemas/LLMProviderHealthInfo'
    LLMProviderHealthInfo:
      type: object
      description: Provider health status
      properties:
        status:
          type: string
          enum:
            - healthy
            - unhealthy
            - unknown
        message:
          type: string
          description: Health check message
        last_checked:
          type: string
          format: date-time
          description: Last health check timestamp
    CreateLLMProviderRequest:
      type: object
      required:
        - name
        - type
      properties:
        name:
          type: string
          description: Unique provider name
        type:
          type: string
          enum:
            - openai
            - azure-openai
            - anthropic
            - bedrock
            - ollama
            - gemini
            - custom
        api_key:
          type: string
          description: API key (mutually exclusive with api_key_secret_arn)
        api_key_secret_arn:
          type: string
          description: AWS Secrets Manager ARN for API key
        endpoint:
          type: string
          description: API endpoint URL
        model:
          type: string
          description: Default model name
        region:
          type: string
          description: AWS region (for Bedrock)
        enabled:
          type: boolean
          default: true
        priority:
          type: integer
          default: 100
        weight:
          type: integer
          default: 100
        rate_limit:
          type: integer
          description: Max requests per second
        timeout_seconds:
          type: integer
          default: 30
        settings:
          type: object
          additionalProperties: true
    UpdateLLMProviderRequest:
      type: object
      description: Partial update - only provided fields are updated
      properties:
        api_key:
          type: string
        api_key_secret_arn:
          type: string
        endpoint:
          type: string
        model:
          type: string
        region:
          type: string
        enabled:
          type: boolean
        priority:
          type: integer
        weight:
          type: integer
        rate_limit:
          type: integer
        timeout_seconds:
          type: integer
        settings:
          type: object
          additionalProperties: true
    LLMProviderResponse:
      type: object
      properties:
        provider:
          $ref: '#/components/schemas/LLMProviderResource'
    PaginationMeta:
      type: object
      description: Pagination metadata for list responses
      properties:
        page:
          type: integer
          description: Current page number (1-indexed)
          example: 1
        page_size:
          type: integer
          description: Number of items per page
          example: 20
        total_items:
          type: integer
          description: Total number of items across all pages
          example: 42
        total_pages:
          type: integer
          description: Total number of pages
          example: 3
    LLMProviderListResponse:
      type: object
      properties:
        providers:
          type: array
          items:
            $ref: '#/components/schemas/LLMProviderResource'
        pagination:
          $ref: '#/components/schemas/PaginationMeta'
    LLMProviderHealthResponse:
      type: object
      properties:
        name:
          type: string
        health:
          $ref: '#/components/schemas/LLMProviderHealthInfo'
    LLMRoutingConfigResponse:
      type: object
      properties:
        weights:
          type: object
          additionalProperties:
            type: integer
          description: Provider name to weight mapping
          example:
            openai: 100
            anthropic: 80
            bedrock: 60
    UpdateLLMRoutingRequest:
      type: object
      required:
        - weights
      properties:
        weights:
          type: object
          additionalProperties:
            type: integer
          description: Provider name to weight mapping
    LLMProviderAPIError:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              enum:
                - NOT_FOUND
                - ALREADY_EXISTS
                - INVALID_REQUEST
                - UNAUTHORIZED
                - INTERNAL_ERROR
            message:
              type: string
    RBIAISystem:
      type: object
      properties:
        id:
          type: string
          format: uuid
        org_id:
          type: string
        system_id:
          type: string
        system_name:
          type: string
        system_version:
          type: string
        description:
          type: string
        risk_category:
          type: string
          enum:
            - low
            - medium
            - high
        deployment_status:
          type: string
          enum:
            - development
            - sandbox
            - canary
            - production
            - deprecated
        model_type:
          type: string
        model_provider:
          type: string
        use_case:
          type: string
        board_approval_required:
          type: boolean
        board_approval_status:
          type: string
          enum:
            - not_required
            - pending
            - approved
            - rejected
            - revoked
        last_validation_date:
          type: string
          format: date
        next_validation_due:
          type: string
          format: date
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    RBIAISystemCreate:
      type: object
      required:
        - system_id
        - system_name
        - risk_category
      properties:
        system_id:
          type: string
        risk_mitigation:
          type: object
        recommendations:
          type: array
          items:
            type: string
        created_by:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        submitted_at:
          type: string
          format: date-time
        submitted_by:
          type: string
        approved_at:
          type: string
          format: date-time
        approved_by:
          type: string
        rejected_at:
          type: string
          format: date-time
        rejected_by:
          type: string
        rejection_reason:
          type: string
    RequirementStatus:
      type: object
      properties:
        requirement_id:
          type: string
        article:
          type: string
          description: EU AI Act article reference (e.g., "Article 9")
        description:
          type: string
        status:
          type: string
          enum:
            - compliant
            - non_compliant
            - partial
            - not_applicable
        notes:
          type: string
        evidence_ids:
          type: array
          items:
            type: string
    EvidenceItem:
      type: object
      properties:
        id:
          type: string
        type:
          type: string
          enum:
            - document
            - test_result
            - audit_log
            - certification
        title:
          type: string
        description:
          type: string
        file_path:
          type: string
        url:
          type: string
        uploaded_at:
          type: string
          format: date-time
        uploaded_by:
          type: string
    Finding:
      type: object
      properties:
        id:
          type: string
        severity:
          type: string
          enum:
            - critical
            - major
            - minor
            - observation
        category:
          type: string
        description:
          type: string
        article:
          type: string
          description: Related EU AI Act article
        remediation:
          type: string
        status:
          type: string
          enum:
            - open
            - resolved
            - accepted
    ConformityAssessment:
      type: object
      description: >
        EU AI Act conformity assessment record (Article 43). Mirrors the

        wire shape of
        `platform/orchestrator/euaiact/types.go::ConformityAssessment`.
      required:
        - id
        - org_id
        - system_id
        - system_name
        - risk_category
        - status
        - assessment_date
        - assessors
        - created_by
        - created_at
        - updated_at
      properties:
        id:
          type: string
          description: Unique assessment identifier (UUID).
        org_id:
          type: string
          description: Owning organization.
        system_id:
          type: string
          description: AI system this assessment covers.
        system_name:
          type: string
          description: Human-readable name of the AI system.
        risk_category:
          type: string
          enum:
            - minimal
            - limited
            - high-risk
            - unacceptable
          description: EU AI Act risk classification.
        status:
          type: string
          enum:
            - draft
            - in_progress
            - submitted
            - approved
            - rejected
        version:
          type: integer
          description: Monotonic version (bumps on every update).
        assessment_date:
          type: string
          format: date-time
        valid_until:
          type: string
          format: date-time
          nullable: true
        assessors:
          type: array
          description: Email addresses of the team members carrying out the assessment.
          items:
            type: string
        requirements:
          type: array
          items:
            $ref: '#/components/schemas/RequirementStatus'
        evidence:
          type: array
          items:
            $ref: '#/components/schemas/EvidenceItem'
        findings:
          type: array
          items:
            $ref: '#/components/schemas/Finding'
        risk_mitigation:
          type: object
          additionalProperties: true
          nullable: true
          description: Free-form risk-mitigation notes structured as a JSON object.
        recommendations:
          type: array
          items:
            type: string
        created_by:
          type: string
          description: User email that opened the assessment.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        submitted_at:
          type: string
          format: date-time
          nullable: true
        submitted_by:
          type: string
        approved_at:
          type: string
          format: date-time
          nullable: true
        approved_by:
          type: string
        rejected_at:
          type: string
          format: date-time
          nullable: true
        rejected_by:
          type: string
        rejection_reason:
          type: string
          description: Free-form reason a reviewer used when transitioning to `rejected`.
    CreateConformityRequest:
      type: object
      description: |
        Request body for `POST /api/v1/euaiact/conformity`. Mirrors
        `platform/orchestrator/euaiact/types.go::CreateAssessmentRequest`.
      required:
        - system_id
        - system_name
        - risk_category
      properties:
        system_id:
          type: string
        system_name:
          type: string
        risk_category:
          type: string
          enum:
            - minimal
            - limited
            - high-risk
            - unacceptable
        assessors:
          type: array
          items:
            type: string
    UpdateConformityRequest:
      type: object
      description: >
        Request body for `PUT /api/v1/euaiact/conformity/{assessment_id}`.

        All fields optional — only the fields present are updated.

        Mirrors
        `platform/orchestrator/euaiact/types.go::UpdateAssessmentRequest`.

        The handler rejects updates when the assessment status is no

        longer `draft` or `in_progress`.
      properties:
        system_name:
          type: string
        risk_category:
          type: string
          enum:
            - minimal
            - limited
            - high-risk
            - unacceptable
        assessors:
          type: array
          items:
            type: string
        requirements:
          type: array
          items:
            $ref: '#/components/schemas/RequirementStatus'
        evidence:
          type: array
          items:
            $ref: '#/components/schemas/EvidenceItem'
        findings:
          type: array
          items:
            $ref: '#/components/schemas/Finding'
        risk_mitigation:
          type: object
          additionalProperties: true
        recommendations:
          type: array
          items:
            type: string
    RecordAccuracyRequest:
      type: object
      required:
        - model_id
        - metric_type
        - value
      properties:
        model_id:
          type: string
        metric_type:
          type: string
          enum:
            - accuracy
            - precision
            - recall
            - f1_score
            - auc_roc
            - auc_pr
            - mse
            - mae
            - custom
        value:
          type: number
          format: double
        sample_size:
          type: integer
        window_start:
          type: string
          format: date-time
        window_end:
          type: string
          format: date-time
        metadata:
          type: object
          additionalProperties: true
    AccuracyMetric:
      type: object
      properties:
        id:
          type: string
        org_id:
          type: string
        model_id:
          type: string
        metric_type:
          type: string
          enum:
            - accuracy
            - precision
            - recall
            - f1_score
            - auc_roc
            - auc_pr
            - mse
            - mae
            - custom
        value:
          type: number
          format: double
        sample_size:
          type: integer
        timestamp:
          type: string
          format: date-time
        window_start:
          type: string
          format: date-time
        window_end:
          type: string
          format: date-time
        metadata:
          type: object
    RecordBiasRequest:
      type: object
      required:
        - model_id
        - category
        - group_a
        - group_b
        - group_a_rate
        - group_b_rate
      properties:
        model_id:
          type: string
        category:
          type: string
          enum:
            - gender
            - age
            - ethnicity
            - disability
            - religion
            - nationality
            - socioeconomic
            - custom
        group_a:
          type: string
          description: Name of the first comparison group
        group_b:
          type: string
          description: Name of the second comparison group
        group_a_rate:
          type: number
          format: double
          description: Positive outcome rate for group A
        group_b_rate:
          type: number
          format: double
          description: Positive outcome rate for group B
        sample_size:
          type: integer
        window_start:
          type: string
          format: date-time
        window_end:
          type: string
          format: date-time
        metadata:
          type: object
          additionalProperties: true
    BiasRecord:
      type: object
      properties:
        id:
          type: string
        org_id:
          type: string
        model_id:
          type: string
        category:
          type: string
          enum:
            - gender
            - age
            - ethnicity
            - disability
            - religion
            - nationality
            - socioeconomic
            - custom
        score:
          type: number
          format: double
          description: Calculated bias score (difference ratio)
        threshold:
          type: number
          format: double
        is_violation:
          type: boolean
        sample_size:
          type: integer
        group_a:
          type: string
        group_b:
          type: string
        group_a_rate:
          type: number
          format: double
        group_b_rate:
          type: number
          format: double
        timestamp:
          type: string
          format: date-time
        window_start:
          type: string
          format: date-time
        window_end:
          type: string
          format: date-time
        metadata:
          type: object
    AccuracySummary:
      type: object
      properties:
        org_id:
          type: string
        total_models:
          type: integer
        models_above_target:
          type: integer
        models_below_target:
          type: integer
        average_accuracy:
          type: number
          format: double
        active_alerts:
          type: integer
        last_updated:
          type: string
          format: date-time
        metrics_by_model:
          type: object
          additionalProperties: true
    AccuracyAlert:
      type: object
      properties:
        id:
          type: string
        org_id:
          type: string
        model_id:
          type: string
        alert_type:
          type: string
          enum:
            - accuracy_degradation
            - bias_detected
        severity:
          type: string
          enum:
            - info
            - warning
            - critical
        title:
          type: string
        description:
          type: string
        metric_type:
          type: string
        bias_category:
          type: string
        current_value:
          type: number
          format: double
        threshold:
          type: number
          format: double
        triggered_at:
          type: string
          format: date-time
        acked_at:
          type: string
          format: date-time
        acked_by:
          type: string
        resolved_at:
          type: string
          format: date-time
        resolved_by:
          type: string
    ExecutionListResponse:
      type: object
      properties:
        executions:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/ExecutionSummary'
        total:
          type: integer
        limit:
          type: integer
        offset:
          type: integer
    ExecutionSummary:
      type: object
      properties:
        request_id:
          type: string
        workflow_name:
          type: string
        status:
          type: string
          enum:
            - pending
            - running
            - completed
            - failed
        total_steps:
          type: integer
        completed_steps:
          type: integer
        started_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
        duration_ms:
          type: integer
        total_tokens:
          type: integer
        total_cost_usd:
          type: number
          format: double
        org_id:
          type: string
        tenant_id:
          type: string
        user_id:
          type: string
        error_message:
          type: string
        input_summary:
          type: string
          description: |
            Truncated, redacted summary of the workflow input — safe
            to display in audit listings without leaking PII or
            secrets. Full input is in the per-step ExecutionSnapshot
            records.
        output_summary:
          type: string
          description: |
            Truncated, redacted summary of the workflow's final
            output. Same redaction posture as `input_summary`.
    ExecutionSnapshot:
      type: object
      properties:
        request_id:
          type: string
        step_index:
          type: integer
        step_name:
          type: string
        status:
          type: string
          enum:
            - pending
            - running
            - completed
            - failed
            - paused
        started_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
        duration_ms:
          type: integer
        input:
          type: object
        output:
          type: object
        provider:
          type: string
        model:
          type: string
        tokens_in:
          type: integer
        tokens_out:
          type: integer
        cost_usd:
          type: number
          format: double
        policies_checked:
          type: array
          items:
            type: string
        policies_triggered:
          type: array
          items:
            $ref: '#/components/schemas/PolicyEvent'
        error_message:
          type: string
        retry_count:
          type: integer
        approval_required:
          type: boolean
          description: |
            True when this step gated on human approval. The pair
            `approved_at` + `approved_by` records when and by whom
            the approval was granted (or shows null on rejection).
        approved_at:
          type: string
          format: date-time
          description: |
            When the approval-required gate was resolved. Pair with
            `approved_by` to compute approval latency at the step level.
        approved_by:
          type: string
          description: |
            User email of the approver. Empty when
            `approval_required` is false or the gate was auto-resolved.
    PolicyEvent:
      type: object
      properties:
        policy_id:
          type: string
        policy_name:
          type: string
        action:
          type: string
          enum:
            - block
            - warn
            - require_approval
        matched:
          type: string
        resolution:
          type: string
    Execution:
      type: object
      properties:
        summary:
          $ref: '#/components/schemas/ExecutionSummary'
        steps:
          type: array
          items:
            $ref: '#/components/schemas/ExecutionSnapshot'
    TimelineEntry:
      type: object
      properties:
        step_index:
          type: integer
        step_name:
          type: string
        status:
          type: string
          enum:
            - pending
            - running
            - completed
            - failed
            - paused
        started_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
        duration_ms:
          type: integer
        has_error:
          type: boolean
        has_approval:
          type: boolean
    ExecutionExport:
      type: object
      properties:
        exported_at:
          type: string
          format: date-time
        format:
          type: string
        execution:
          $ref: '#/components/schemas/Execution'
    Budget:
      type: object
      required:
        - id
        - name
        - scope
        - limit_usd
        - period
      properties:
        id:
          type: string
          description: Unique budget identifier
        name:
          type: string
          description: Human-readable budget name
        enabled:
          type: boolean
          default: true
          description: |
            When false the budget is configured but its limit is not
            enforced — usage is still tracked. Lets operators dry-run
            a budget before flipping it on.
        scope:
          type: string
          enum:
            - organization
            - team
            - agent
            - workflow
            - user
          description: Budget scope level
        scope_id:
          type: string
          description: ID of the scoped entity (team_id, agent_id, etc.)
        limit_usd:
          type: number
          format: double
          description: Maximum spending limit in USD
        period:
          type: string
          enum:
            - daily
            - weekly
            - monthly
            - quarterly
            - yearly
          description: Budget reset period
        on_exceed:
          type: string
          enum:
            - warn
            - block
            - downgrade
          default: warn
          description: Action when budget is exceeded
        alert_thresholds:
          type: array
          items:
            type: integer
          description: Percentage thresholds for alerts (e.g., [50, 80, 100])
        org_id:
          type: string
          description: Organization ID
        tenant_id:
          type: string
          description: Tenant ID (multi-tenant deployments)
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    BudgetCreate:
      type: object
      required:
        - id
        - name
        - scope
        - limit_usd
        - period
      properties:
        id:
          type: string
        name:
          type: string
        scope:
          type: string
          enum:
            - organization
            - team
            - agent
            - workflow
            - user
        scope_id:
          type: string
        limit_usd:
          type: number
          format: double
        period:
          type: string
          enum:
            - daily
            - weekly
            - monthly
            - quarterly
            - yearly
        on_exceed:
          type: string
          enum:
            - warn
            - block
            - downgrade
          default: warn
        alert_thresholds:
          type: array
          items:
            type: integer
    BudgetUpdate:
      type: object
      properties:
        name:
          type: string
        limit_usd:
          type: number
          format: double
        on_exceed:
          type: string
          enum:
            - warn
            - block
            - downgrade
        alert_thresholds:
          type: array
          items:
            type: integer
    BudgetList:
      type: object
      properties:
        budgets:
          type: array
          items:
            $ref: '#/components/schemas/Budget'
        count:
          type: integer
    BudgetStatus:
      type: object
      properties:
        budget:
          $ref: '#/components/schemas/Budget'
        used_usd:
          type: number
          format: double
          description: Amount spent in current period
        remaining_usd:
          type: number
          format: double
          description: Remaining budget
        percentage:
          type: number
          format: double
          description: Percentage of budget used
        period_start:
          type: string
          format: date-time
        period_end:
          type: string
          format: date-time
        is_exceeded:
          type: boolean
          description: Whether budget limit has been exceeded
        is_blocked:
          type: boolean
          description: Whether requests are being blocked
    BudgetAlert:
      type: object
      properties:
        id:
          type: integer
        budget_id:
          type: string
        threshold:
          type: integer
          description: Threshold percentage that triggered alert
        percentage_reached:
          type: number
          format: double
        amount_usd:
          type: number
          format: double
        alert_type:
          type: string
          enum:
            - threshold_reached
            - budget_exceeded
            - budget_blocked
        message:
          type: string
        created_at:
          type: string
          format: date-time
        acknowledged:
          type: boolean
    BudgetAlertList:
      type: object
      properties:
        alerts:
          type: array
          items:
            $ref: '#/components/schemas/BudgetAlert'
        count:
          type: integer
    BudgetCheckRequest:
      type: object
      required:
        - org_id
      properties:
        org_id:
          type: string
        team_id:
          type: string
        agent_id:
          type: string
        workflow_id:
          type: string
        user_id:
          type: string
    BudgetCheckResponse:
      type: object
      properties:
        allowed:
          type: boolean
          description: Whether the request should be allowed
        action:
          type: string
          enum:
            - warn
            - block
            - downgrade
          description: Action that was taken (if budget exceeded)
        budget_id:
          type: string
          description: ID of the budget that blocked the request
        budget_name:
          type: string
        used_usd:
          type: number
          format: double
        limit_usd:
          type: number
          format: double
        percentage:
          type: number
          format: double
        message:
          type: string
    UsageSummary:
      type: object
      properties:
        total_cost_usd:
          type: number
          format: double
        total_tokens_in:
          type: integer
        total_tokens_out:
          type: integer
        total_requests:
          type: integer
        average_cost_per_request:
          type: number
          format: double
        period_start:
          type: string
          format: date-time
        period_end:
          type: string
          format: date-time
        period:
          type: string
          description: |
            Bucket label for the rolled-up period (e.g. `2026-04`,
            `2026-W17`, `2026-04-29`). Echoes the request's bucket
            granularity so callers can label charts without parsing
            `period_start` / `period_end`.
    UsageBreakdownItem:
      type: object
      properties:
        group_by:
          type: string
          description: Dimension name (provider, model, agent, etc.)
        group_value:
          type: string
          description: Value of the dimension
        cost_usd:
          type: number
          format: double
        tokens_in:
          type: integer
        tokens_out:
          type: integer
        request_count:
          type: integer
        percentage:
          type: number
          format: double
    UsageBreakdown:
      type: object
      properties:
        group_by:
          type: string
        total_cost_usd:
          type: number
          format: double
        items:
          type: array
          items:
            $ref: '#/components/schemas/UsageBreakdownItem'
        period:
          type: string
          description: |
            Bucket label for the rolled-up period (e.g. `2026-04`,
            `2026-W17`). Same field as `UsageSummary.period`.
        period_start:
          type: string
          format: date-time
          description: Inclusive start of the rolled-up period.
        period_end:
          type: string
          format: date-time
          description: Exclusive end of the rolled-up period.
    UsageRecord:
      type: object
      properties:
        id:
          type: string
        request_id:
          type: string
        org_id:
          type: string
        tenant_id:
          type: string
        team_id:
          type: string
        agent_id:
          type: string
        user_id:
          type: string
        workflow_id:
          type: string
        provider:
          type: string
        model:
          type: string
        tokens_in:
          type: integer
        tokens_out:
          type: integer
        cost_usd:
          type: number
          format: double
        latency_ms:
          type: integer
        success:
          type: boolean
        error_message:
          type: string
        created_at:
          type: string
          format: date-time
        timestamp:
          type: string
          format: date-time
          description: |
            Wall-clock time the usage event occurred — distinct from
            `created_at` (when the row was inserted). Some downstream
            paths emit this as the canonical event time.
    UsageRecordList:
      type: object
      properties:
        records:
          type: array
          items:
            $ref: '#/components/schemas/UsageRecord'
        count:
          type: integer
        total:
          type: integer
    ModelPricing:
      type: object
      properties:
        input_per_1k:
          type: number
          format: double
          description: Cost per 1,000 input tokens in USD
        output_per_1k:
          type: number
          format: double
          description: Cost per 1,000 output tokens in USD
    PricingInfo:
      type: object
      required:
        - provider
        - model
        - pricing
      properties:
        provider:
          type: string
        model:
          type: string
        pricing:
          $ref: '#/components/schemas/ModelPricing'
    PricingList:
      type: object
      required:
        - providers
      properties:
        providers:
          type: object
          additionalProperties:
            type: object
            additionalProperties:
              $ref: '#/components/schemas/ModelPricing'
    MediaAnalysisItemResponse:
      type: object
      properties:
        media_index:
          type: integer
          description: Index of the media item in the request
        sha256_hash:
          type: string
          description: SHA-256 hash of the image data
        has_faces:
          type: boolean
          description: Whether faces were detected
        face_count:
          type: integer
          description: Number of faces detected
        has_biometric_data:
          type: boolean
          description: Whether biometric data was detected (GDPR Art. 9)
        nsfw_score:
          type: number
          format: double
          minimum: 0
          maximum: 1
          description: NSFW content score (0.0-1.0)
        violence_score:
          type: number
          format: double
          minimum: 0
          maximum: 1
          description: Violence content score (0.0-1.0)
        content_safe:
          type: boolean
          description: Aggregated content safety flag
        document_type:
          type: string
          description: Classified document type (e.g., id_card, passport, bank_statement)
        is_sensitive_document:
          type: boolean
          description: Whether the document is classified as sensitive
        has_pii:
          type: boolean
          description: Whether PII was detected in image text (via OCR)
        pii_types:
          type: array
          items:
            type: string
          description: Types of PII detected (e.g., credit_card, ssn, email)
        has_extracted_text:
          type: boolean
          description: Whether text was extracted from the image via OCR
        extracted_text_length:
          type: integer
          description: Length of extracted text in characters (0 if none)
        estimated_cost_usd:
          type: number
          format: double
          description: Estimated analysis cost for this media item
        warnings:
          type: array
          items:
            type: string
          description: Governance warnings for this media item
        structured_warnings:
          type: array
          items:
            type: object
            properties:
              code:
                type: string
                description: Warning code identifier
              message:
                type: string
                description: Human-readable warning message
          description: Structured governance warnings with codes
    MediaAnalysisResponse:
      type: object
      properties:
        results:
          type: array
          items:
            $ref: '#/components/schemas/MediaAnalysisItemResponse'
        total_cost_usd:
          type: number
          format: double
          description: Total cost of media analysis across all items
        analysis_time_ms:
          type: integer
          format: int64
          description: Total analysis time in milliseconds
    MediaContentRequest:
      type: object
      required:
        - source
        - mime_type
      properties:
        source:
          type: string
          enum:
            - base64
            - url
          description: How the media is provided
        base64_data:
          type: string
          description: Base64-encoded image data (required when source=base64)
        url:
          type: string
          format: uri
          description: URL to image (required when source=url)
        mime_type:
          type: string
          enum:
            - image/jpeg
            - image/png
            - image/gif
            - image/webp
          description: Media content type
    MediaGovernanceConfig:
      type: object
      properties:
        tenant_id:
          type: string
          description: Tenant identifier
        enabled:
          type: boolean
          description: Whether media governance is enabled for this tenant
        allowed_analyzers:
          type: array
          items:
            type: string
          description: List of allowed analyzer types (empty means all available)
        updated_at:
          type: string
          format: date-time
          description: Last update timestamp
        updated_by:
          type: string
          description: User who last updated the configuration
    MediaGovernanceConfigUpdate:
      type: object
      properties:
        enabled:
          type: boolean
          description: Enable or disable media governance
        allowed_analyzers:
          type: array
          items:
            type: string
          description: Restrict to specific analyzer types
    MediaGovernanceStatus:
      type: object
      properties:
        available:
          type: boolean
          description: Whether media governance is available for the current tier
        enabled_by_default:
          type: boolean
          description: Whether media governance is enabled by default for the current tier
        per_tenant_control:
          type: boolean
          description: Whether per-tenant configuration is available
        tier:
          type: string
          description: Current license tier
    MediaAuditRecord:
      type: object
      description: One media-governance audit row
      properties:
        request_id:
          type: string
        tenant_id:
          type: string
        timestamp:
          type: string
          format: date-time
        media_type:
          type: string
        analysis_results:
          type: object
          additionalProperties: true
        policy_actions:
          type: array
          items:
            type: string
        blocked:
          type: boolean
    AuditActionReport:
      type: object
      description: |
        Per-action aggregate for a window. by_action always carries the full
        canonical verdict set (allowed / blocked / redacted / needs_approval /
        error) — a verdict with no rows reports 0 rather than being absent.
      properties:
        tenant_id:
          type: string
        user_email:
          type: string
          description: Echoed filter; omitted when not filtered
        start_time:
          type: string
          format: date-time
        end_time:
          type: string
          format: date-time
        total:
          type: integer
          description: Governed decisions in the window (lifecycle events excluded)
        by_action:
          type: object
          additionalProperties:
            type: integer
          description: Counts folded onto the canonical verdicts
        avg_latency_ms:
          type: number
          format: double
        top_policies:
          type: array
          description: Top 10 policies by trigger count
          items:
            type: object
            properties:
              policy_name:
                type: string
              trigger_count:
                type: integer
              block_count:
                type: integer
    SessionSummaryResponse:
      type: object
      description: GET /api/v1/audit/session-summary payload (Enterprise)
      properties:
        tenant_id:
          type: string
        user_email:
          type: string
          description: Echoed filter; omitted when not filtered
        start_date:
          type: string
          format: date
        end_date:
          type: string
          format: date
        buckets:
          type: array
          items:
            $ref: '#/components/schemas/SessionSummaryBucket'
        bucket_limit:
          type: integer
          description: Effective bucket cap applied to this response
        truncated:
          type: boolean
          description: |
            True when the window held more buckets than bucket_limit —
            narrow the window or raise ?limit=
    SessionSummaryBucket:
      type: object
      description: |
        One session (or per-user-day fallback) bucket. Exactly one of
        session_id / day is set: session_id for a real session bucket, day
        (YYYY-MM-DD) for rows without a session id grouped by calendar day.
      properties:
        session_id:
          type: string
          description: Set when the bucket is a real session
        day:
          type: string
          description: Set only for a per-user-day fallback bucket (YYYY-MM-DD)
        user_email:
          type: string
        tenant_id:
          type: string
        start_time:
          type: string
          format: date-time
        end_time:
          type: string
          format: date-time
        total:
          type: integer
        by_action:
          type: object
          additionalProperties:
            type: integer
          description: Per-bucket counts folded onto the canonical verdicts
        tools:
          type: array
          description: Per-request_type usage within the bucket
          items:
            type: object
            properties:
              request_type:
                type: string
              count:
                type: integer
              tokens_used:
                type: integer
              cost:
                type: number
                format: double
              avg_latency_ms:
                type: number
                format: double
        tokens_used:
          type: integer
          description: >-
            Per-request token sum over governed audit rows (authoritative
            gateway view)
        cost:
          type: number
          format: double
        avg_latency_ms:
          type: number
          format: double
        usage_metrics:
          $ref: '#/components/schemas/SessionSummaryUsageMetrics'
    SessionSummaryUsageMetrics:
      type: object
      description: |
        Optional Claude-export enrichment (#2852), sourced from the OTLP
        metrics ingest. Absent when no metric rows match the bucket. These
        are the CLI's own aggregates (everything the tool did, governed or
        not) and are deliberately nested rather than merged with the
        bucket-level tokens_used/cost, which remain the governed-gateway
        sums. tokens_used here counts input/output only; cache tokens are
        kept separately in cache_tokens and never inflate the headline.
      properties:
        lines_of_code:
          type: integer
        active_time_seconds:
          type: number
          format: double
        commits:
          type: integer
        pull_requests:
          type: integer
        tool_permission_decisions:
          type: object
          properties:
            accept:
              type: integer
            reject:
              type: integer
        session_count:
          type: integer
        tokens_used:
          type: integer
        cache_tokens:
          type: integer
        cost_usd:
          type: number
          format: double
    CreateOverrideRequest:
      type: object
      required:
        - policy_id
        - policy_type
        - override_reason
      properties:
        policy_id:
          type: string
          description: Policy UUID, or the human-readable slug (static) / name (dynamic)
        policy_type:
          type: string
          enum:
            - static
            - dynamic
        tool_signature:
          type: string
          description: Optional tool scope for the override
        override_reason:
          type: string
          maxLength: 500
          description: Mandatory justification (ADR-044)
        ttl_seconds:
          type: integer
          format: int64
          description: |
            Override lifetime in seconds. Omitted/0 defaults to 3600.
            Clamped server-side to [60, 86400]; the response reports any clamp.
    CreateOverrideResponse:
      type: object
      properties:
        id:
          type: string
        policy_id:
          type: string
          description: Echoes the value sent in the request (not resolved to a UUID)
        policy_type:
          type: string
        expires_at:
          type: string
          format: date-time
        ttl_seconds:
          type: integer
          format: int64
          description: Effective TTL after clamping
        requested_ttl:
          type: integer
          format: int64
          description: Present when the requested TTL was clamped
        clamped:
          type: boolean
          description: Present (true) when the TTL was clamped
        clamped_reason:
          type: string
          description: exceeds_hard_cap or below_minimum; present when clamped
        created_at:
          type: string
          format: date-time
    OverrideSummary:
      type: object
      properties:
        id:
          type: string
        policy_id:
          type: string
        policy_type:
          type: string
        tenant_id:
          type: string
        override_reason:
          type: string
        expires_at:
          type: string
          format: date-time
        revoked_at:
          type: string
          format: date-time
          description: Present only on revoked overrides
        created_at:
          type: string
          format: date-time
    OverrideDetail:
      type: object
      properties:
        id:
          type: string
        policy_id:
          type: string
        policy_type:
          type: string
        tenant_id:
          type: string
        organization_id:
          type: string
        tool_signature:
          type: string
        override_reason:
          type: string
        expires_at:
          type: string
          format: date-time
        created_by:
          type: string
        created_at:
          type: string
          format: date-time
        revoked_at:
          type: string
          format: date-time
        revoked_by:
          type: string
    DecisionListResponse:
      type: object
      properties:
        decisions:
          type: array
          description: Always an array (`[]` when empty)
          items:
            $ref: '#/components/schemas/DecisionListItem'
    DecisionListItem:
      type: object
      properties:
        decision_id:
          type: string
        timestamp:
          type: string
          format: date-time
        decision:
          type: string
          description: Canonical verdict
          enum:
            - allowed
            - blocked
            - redacted
            - needs_approval
            - error
        policy_id:
          type: string
        tool_signature:
          type: string
        context:
          type: object
          additionalProperties:
            type: string
          description: Decision context, truncated to at most 5 keys (sorted)
        transfer_basis:
          type: string
          description: UU PDP Pasal 56 transfer basis; Enterprise cross-border rows only
        data_residency:
          type: string
          description: ISO 3166-1 alpha-2 destination country; Enterprise only
    DecisionListLimitEnvelope:
      type: object
      description: |
        Tier-limit envelope returned with 429 when the requested page size
        exceeds the tier cap. Same shape as the plugin rate-limit envelopes.
      properties:
        error:
          type: string
          example: >-
            Free tier shows the last 5 decisions in 24h. Pro raises this to 100
            decisions in the last 30 days.
        limit_type:
          type: string
          example: decision_list_size
        tier:
          type: string
          description: Effective tier the request was evaluated under
        limit:
          type: integer
          description: The tier's page cap
        remaining:
          type: integer
          example: 0
        upgrade:
          type: object
          properties:
            tier:
              type: string
              example: Pro
            wording:
              type: string
            compare_url:
              type: string
              example: https://getaxonflow.com/pricing/
            buy_url:
              type: string
    DecisionExplanation:
      type: object
      description: ADR-043 decision explanation
      properties:
        decision_id:
          type: string
        timestamp:
          type: string
          format: date-time
        policy_matches:
          type: array
          description: Always an array (`[]` when empty)
          items:
            type: object
            properties:
              policy_id:
                type: string
              policy_name:
                type: string
              action:
                type: string
              risk_level:
                type: string
              allow_override:
                type: boolean
              policy_description:
                type: string
        matched_rules:
          type: array
          description: Per-rule match detail; omitted when unavailable
          items:
            type: object
            properties:
              policy_id:
                type: string
              rule_id:
                type: string
              rule_text:
                type: string
              matched_on:
                type: string
        decision:
          type: string
          description: |
            Verdict echoed verbatim from the audit row's policy_decision
            column (not normalized), so historical spellings can appear
        reason:
          type: string
        risk_level:
          type: string
        override_available:
          type: boolean
          description: |
            True when at least one matched policy has allow_override=true
            and is not critical-risk
        override_existing_id:
          type: string
          description: >-
            Id of an already-active override covering this decision, when one
            exists
        historical_hit_count_session:
          type: integer
          description: Rolling-24h hit count for the first matched policy by this user
        policy_source_link:
          type: string
        tool_signature:
          type: string
        policy_version_at_decision:
          type: integer
          description: Policy version recorded at decision time; omitted when unknown
        latest_policy_version:
          type: integer
          description: Current head version of the first matched (static) policy
        context:
          type: object
          additionalProperties:
            type: string
        context_truncated:
          type: boolean
    OJKAPIError:
      type: object
      description: OJK module error envelope
      properties:
        code:
          type: string
          description: |
            Machine-readable error code (e.g. missing_tenant,
            invalid_request, validation_error, missing_export_id, not_found,
            invalid_transition, internal_error, method_not_allowed)
        message:
          type: string
        details:
          type: string
          description: Omitted when empty
    OJKAuditExportRequest:
      type: object
      required:
        - start_date
        - end_date
      properties:
        start_date:
          type: string
          format: date
          description: Window start (YYYY-MM-DD)
        end_date:
          type: string
          format: date
          description: >-
            Window end (YYYY-MM-DD, not before start_date; range at most 5
            years)
        format:
          type: string
          enum:
            - json
            - csv
            - xml
          default: json
        framework:
          type: string
          enum:
            - OJK_AI_GOVERNANCE
            - UU_PDP
            - BI_PJP
            - OJK_BI_COMBINED
          default: OJK_BI_COMBINED
        data_types:
          type: array
          description: Defaults to [all]
          items:
            type: string
            enum:
              - policy_violations
              - llm_calls
              - decision_chain
              - hitl_oversight
              - pii_redactions
              - cross_border_transfers
              - breach_notifications
              - all
        filters:
          type: object
          properties:
            agent_ids:
              type: array
              items:
                type: string
            user_ids:
              type: array
              items:
                type: string
            severity:
              type: array
              items:
                type: string
            policy_types:
              type: array
              items:
                type: string
        include_pii:
          type: boolean
          default: false
    OJKAuditExportResponse:
      type: object
      properties:
        export_id:
          type: string
        status:
          type: string
          description: Always `completed` (the export runs synchronously)
        framework:
          type: string
          enum:
            - OJK_AI_GOVERNANCE
            - UU_PDP
            - BI_PJP
            - OJK_BI_COMBINED
        format:
          type: string
          enum:
            - json
            - csv
            - xml
        summary:
          type: object
          properties:
            total_records:
              type: integer
            records_by_type:
              type: object
              additionalProperties:
                type: integer
            date_range:
              type: object
              properties:
                start:
                  type: string
                  format: date-time
                end:
                  type: string
                  format: date-time
            compliance_score:
              type: number
              format: double
        data:
          type: object
          description: |
            Inline export payload; each key is present only when its data
            type was requested and rows exist
          properties:
            policy_violations:
              type: array
              items:
                type: object
                properties:
                  id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  policy_id:
                    type: string
                  policy_name:
                    type: string
                  severity:
                    type: string
                  action:
                    type: string
                  description:
                    type: string
                  tenant_id:
                    type: string
            llm_calls:
              type: array
              items:
                type: object
                properties:
                  id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  model_id:
                    type: string
                  provider:
                    type: string
                  input_tokens:
                    type: integer
                  output_tokens:
                    type: integer
                  cost:
                    type: number
                    format: double
                  latency_ms:
                    type: integer
                    format: int64
                  policy_decision:
                    type: string
            decision_chains:
              type: array
              items:
                type: object
                properties:
                  id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  decision_id:
                    type: string
                  risk_level:
                    type: string
                  model_id:
                    type: string
                  requires_review:
                    type: boolean
            hitl_records:
              type: array
              description: |
                Declared on the wire type but NEVER populated — the export
                service has no hitl_oversight query implementation (field is
                omitempty, so it is absent from real responses).
              items:
                type: object
                properties:
                  id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  trigger_reason:
                    type: string
                  reviewer_id:
                    type: string
                  decision:
                    type: string
                  review_time_ms:
                    type: integer
                    format: int64
            pii_redactions:
              type: array
              description: |
                Declared on the wire type but NEVER populated — the export
                service has no pii_redactions query implementation (field is
                omitempty, so it is absent from real responses).
              items:
                type: object
                properties:
                  id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  pii_type:
                    type: string
                  redaction_method:
                    type: string
                  confidence:
                    type: number
                    format: double
            cross_border_transfers:
              type: array
              items:
                type: object
                properties:
                  id:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  data_residency:
                    type: string
                  transfer_basis:
                    type: string
                    enum:
                      - adequacy
                      - safeguards
                      - pasal_56b_dpa
                      - consent
                  destination_country:
                    type: string
                  data_categories:
                    type: array
                    items:
                      type: string
                  approval_status:
                    type: string
            breach_notifications:
              type: array
              items:
                type: object
                properties:
                  id:
                    type: string
                  incident_timestamp:
                    type: string
                    format: date-time
                  discovery_time:
                    type: string
                    format: date-time
                  notification_deadline:
                    type: string
                    format: date-time
                  data_subjects_affected:
                    type: integer
                  data_types_involved:
                    type: array
                    items:
                      type: string
                  notified_authority:
                    type: string
                  status:
                    type: string
                    description: Effective status after deadline evaluation
                  stored_status:
                    type: string
                  submitted_at:
                    type: string
                    format: date-time
                  acknowledged_at:
                    type: string
                    format: date-time
                  within_deadline:
                    type: boolean
                  created_at:
                    type: string
                    format: date-time
        download_url:
          type: string
          description: Omitted for inline exports
        created_at:
          type: string
          format: date-time
        expires_at:
          type: string
          format: date-time
        metadata:
          type: object
          properties:
            export_version:
              type: string
              example: 1.0.0
            generated_by:
              type: string
              example: axonflow-ojk-module
            tenant_id:
              type: string
            checksum:
              type: string
              description: SHA-256 hex of the export data
    OJKRetentionStatusResponse:
      type: object
      properties:
        compliance_status:
          type: string
          enum:
            - compliant
            - non_compliant
        framework:
          type: string
          example: OJK_BI_COMBINED
        retention_days:
          type: integer
        min_retention_days:
          type: integer
          example: 1825
        data_types:
          type: array
          description: |
            ⚠️ Always empty in the current implementation — per-type
            retention reporting is not implemented (`GetRetentionStatus`
            returns a fixed empty slice).
          items:
            type: object
            properties:
              data_type:
                type: string
              status:
                type: string
              oldest_record:
                type: string
                format: date-time
              newest_record:
                type: string
                format: date-time
              total_records:
                type: integer
                format: int64
        next_cleanup:
          type: string
          format: date-time
          description: Never set by the current implementation (omitted)
    OJKComplianceReadinessResponse:
      type: object
      properties:
        ready:
          type: boolean
          description: True at score 80+
        score:
          type: integer
          minimum: 0
          maximum: 100
        framework:
          type: string
          example: OJK_BI_COMBINED
        checks:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              description:
                type: string
              status:
                type: string
                enum:
                  - pass
                  - fail
                  - warning
              details:
                type: string
        recommendations:
          type: array
          items:
            type: string
    OJKBreachNotification:
      type: object
      description: |
        UU PDP Art. 46 breach notification. On submission the server assigns
        id / created_at / submitted_at and computes notification_deadline
        (discovery_time + 72h) — client-supplied values for those fields
        are ignored. notified_authority defaults to MOCDA only when the
        client omits it; a supplied value is honored.
      required:
        - incident_timestamp
        - discovery_time
        - data_subjects_affected
        - data_types_involved
        - description
        - remediation_steps
      properties:
        id:
          type: string
          description: Server-assigned
        incident_timestamp:
          type: string
          format: date-time
        discovery_time:
          type: string
          format: date-time
        notification_deadline:
          type: string
          format: date-time
          description: Server-computed (discovery_time + 72h)
        data_subjects_affected:
          type: integer
          minimum: 1
        data_types_involved:
          type: array
          minItems: 1
          items:
            type: string
        description:
          type: string
        remediation_steps:
          type: array
          minItems: 1
          items:
            type: string
        notified_authority:
          type: string
          description: Defaults to MOCDA
        status:
          type: string
          enum:
            - draft
            - submitted
            - acknowledged
            - overdue
            - failed
        submitted_at:
          type: string
          format: date-time
        acknowledged_at:
          type: string
          format: date-time
        created_at:
          type: string
          format: date-time
    OJKDashboardResponse:
      type: object
      properties:
        framework:
          type: string
          example: OJK_BI_COMBINED
        compliance_score:
          type: integer
        total_audit_records:
          type: integer
          format: int64
        active_policies:
          type: integer
        recent_violations:
          type: integer
        retention_status:
          type: string
        breach_notifications:
          type: integer
        overdue_breach_notifications:
          type: integer
        last_updated:
          type: string
          format: date-time
    UnifiedExecutionListResponse:
      type: object
      properties:
        executions:
          type: array
          items:
            $ref: '#/components/schemas/UnifiedExecutionStatus'
        total:
          type: integer
        limit:
          type: integer
        offset:
          type: integer
        has_more:
          type: boolean
    UnifiedExecutionStatus:
      type: object
      description: |
        Unified status record covering both MAP plans and WCP workflows.
        Distinct from the replay Execution schema and from the MAP
        StepStatus schema.
      properties:
        execution_id:
          type: string
        execution_type:
          type: string
          enum:
            - map_plan
            - wcp_workflow
        name:
          type: string
        source:
          type: string
        status:
          type: string
          enum:
            - pending
            - running
            - completed
            - failed
            - cancelled
            - aborted
            - expired
        current_step_index:
          type: integer
        total_steps:
          type: integer
        progress_percent:
          type: number
          format: double
        started_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
        duration:
          type: string
        estimated_cost_usd:
          type: number
          format: double
        actual_cost_usd:
          type: number
          format: double
        steps:
          type: array
          items:
            $ref: '#/components/schemas/UnifiedStepStatus'
        error:
          type: string
        tenant_id:
          type: string
        org_id:
          type: string
        user_id:
          type: string
        client_id:
          type: string
        metadata:
          type: object
          additionalProperties: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    UnifiedStepStatus:
      type: object
      properties:
        step_id:
          type: string
        step_index:
          type: integer
        step_name:
          type: string
        step_type:
          type: string
          enum:
            - llm_call
            - tool_call
            - connector_call
            - human_task
            - synthesis
            - action
            - gate
        status:
          type: string
          enum:
            - pending
            - running
            - completed
            - failed
            - skipped
            - blocked
            - approval
        started_at:
          type: string
          format: date-time
        ended_at:
          type: string
          format: date-time
        duration:
          type: string
        decision:
          type: string
          enum:
            - allow
            - block
            - require_approval
        decision_reason:
          type: string
        policies_matched:
          type: array
          items:
            type: string
        approval_status:
          type: string
          enum:
            - pending
            - approved
            - rejected
            - expired
        approved_by:
          type: string
        approved_at:
          type: string
          format: date-time
        rejected_by:
          type: string
        rejected_at:
          type: string
          format: date-time
        model:
          type: string
        provider:
          type: string
        cost_usd:
          type: number
          format: double
        tokens_in:
          type: integer
        tokens_out:
          type: integer
        input:
          description: Raw step input (JSON)
        output:
          description: Raw step output (JSON)
        result_summary:
          type: string
        error:
          type: string
  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error: Invalid request body
    Unauthorized:
      description: Unauthorized - missing or invalid tenant ID
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/LLMProviderAPIError'
          example:
            error:
              code: UNAUTHORIZED
              message: missing tenant ID
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error: Resource not found
    Forbidden:
      description: Forbidden - insufficient permissions or enterprise license required
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error: Enterprise license required
    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            error: Internal server error
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
      description: OAuth2-style client credentials (clientId:clientSecret)
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Enterprise JWT token (see /scripts/generate-jwt.sh)
