openapi: "3.1.0"
info:
  title: BrainstormRouter API
  version: "1.0.0"
  description: |
    Autonomous AI API Engine — multi-tenant gateway that intelligently routes
    AI traffic across providers with built-in memory, cost optimization, and
    autonomous background processing.

    All `/v1/*` endpoints require an API key via `Authorization: Bearer <key>`.
  contact:
    name: BrainstormRouter
    url: https://brainstormrouter.com
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary

tags:
  - name: MNI
    description: Machine-Native Interface — agent self-awareness, error recovery, and health transparency

servers:
  - url: https://api.brainstormrouter.com
    description: Production

security:
  - BearerAuth: []

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: "API key (e.g. `br_live_...`)"
    SupabaseJWT:
      type: http
      scheme: bearer
      description: "Supabase JWT token (dashboard auth)"

  schemas:
    Message:
      type: object
      required: [role, content]
      properties:
        role:
          type: string
          enum: [system, user, assistant]
        content:
          oneOf:
            - type: string
            - type: array
              items:
                type: object

    ChatCompletionRequest:
      type: object
      required: [model, messages]
      properties:
        model:
          type: string
          description: 'Model ID, alias, or `"auto"` for intelligent routing'
          examples: ["auto", "anthropic/claude-sonnet-4-20250514", "gpt-4o"]
        messages:
          type: array
          items:
            $ref: "#/components/schemas/Message"
        stream:
          type: boolean
          default: false
        temperature:
          type: number
          minimum: 0
          maximum: 2
        max_tokens:
          type: integer
        top_p:
          type: number
        stop:
          oneOf:
            - type: string
            - type: array
              items:
                type: string
        tools:
          type: array
          items:
            type: object
            properties:
              type:
                type: string
                enum: [function]
              function:
                type: object
                required: [name]
                properties:
                  name:
                    type: string
                  description:
                    type: string
                  parameters:
                    type: object
        conversation_id:
          type: string
          description: Thread multiple requests into a conversation for memory
        user:
          type: string
          description: End-user or agent identifier

    ChatCompletionResponse:
      type: object
      properties:
        id:
          type: string
        object:
          type: string
          enum: [chat.completion]
        created:
          type: integer
        model:
          type: string
        choices:
          type: array
          items:
            type: object
            properties:
              index:
                type: integer
              message:
                $ref: "#/components/schemas/Message"
              finish_reason:
                type: string
                enum: [stop, tool_calls, length]
        usage:
          type: object
          properties:
            prompt_tokens:
              type: integer
            completion_tokens:
              type: integer
            total_tokens:
              type: integer

    ModelList:
      type: object
      properties:
        object:
          type: string
          enum: [list]
        data:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              object:
                type: string
                enum: [model]
              owned_by:
                type: string

    AliasMap:
      type: object
      additionalProperties:
        type: string
      examples:
        - fast: anthropic/claude-haiku-4-5
          research: perplexity/sonar-pro

    UsageSummary:
      type: object
      properties:
        period:
          type: string
        since:
          type: string
          format: date-time
        data:
          type: array
          items:
            type: object
            properties:
              request_count:
                type: integer
              total_input_tokens:
                type: integer
              total_output_tokens:
                type: integer
              total_cost_usd:
                type: number
              avg_latency_ms:
                type: number
              provider:
                type: string
              model:
                type: string

    XdrDestination:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        type:
          type: string
          enum: [crowdstrike, sentinel, splunk-ocsf, cortex, datadog-ocsf]
        enabled:
          type: boolean
        event_types:
          type: array
          items:
            type: string
          description: |
            Optional list of explicit event-type names the destination
            subscribes to. Empty array (default) means the destination
            receives all event types not excluded by `filter_rules`.
        config:
          type: object
          description: Adapter-specific config. Secret fields are redacted in responses.
        filter_rules:
          oneOf:
            - $ref: "#/components/schemas/DestinationFilterRules"
            - type: "null"
        batch:
          $ref: "#/components/schemas/DestinationBatchConfig"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        stats:
          type: object
          description: Per-destination delivery counters maintained by the dispatcher.
          properties:
            last_delivered_at:
              type: string
              format: date-time
              nullable: true
            delivery_count:
              type: integer
              description: Lifetime successful deliveries.
            error_count:
              type: integer
              description: Lifetime failed deliveries (after retry exhaustion).
            last_error:
              type: string
              nullable: true
              description: Most recent adapter error message.

    DestinationBatchConfig:
      type: object
      nullable: true
      description: |
        Optional batching policy. When unset, events are delivered one-per-HTTP.
      properties:
        max_events:
          type: integer
          minimum: 1
          description: Maximum events per batch.
        max_wait_ms:
          type: integer
          minimum: 0
          description: Maximum time to wait before flushing a partial batch.

    DestinationFilterRules:
      type: object
      properties:
        include:
          type: array
          items:
            type: string
          description: |
            List of glob patterns matched against the PlatformEventType.
            `*` matches `[^.]+` (one segment). Empty/absent = match all.
            Example: `intelligence.*` matches `intelligence.budget.exceeded`
            but not `intelligence.cache.eviction_pressure`.
        exclude:
          type: array
          items:
            type: string
          description: List of glob patterns. Applied after `include`.
        min_severity:
          type: integer
          minimum: 1
          maximum: 5
          description: |
            OCSF severity ID minimum. 1=Informational, 2=Low, 3=Medium,
            4=High, 5=Critical. Applied after type filters.

    Error:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [message]
          properties:
            message:
              type: string
            type:
              type: string
            code:
              type: string

    BudgetPeriodStatus:
      type: object
      properties:
        spent_usd:
          type: number
        limit_usd:
          type: number
          nullable: true
        remaining_usd:
          type: number
          nullable: true
        period_key:
          type: string

    BudgetForecastPeriod:
      type: object
      properties:
        spent_usd:
          type: number
        projected_usd:
          type: number
        limit_usd:
          type: number
          nullable: true
        depletion_hour:
          type: number
          nullable: true
        depletion_day:
          type: integer
          nullable: true
        on_track:
          type: boolean
          nullable: true

    KillSwitchStatus:
      type: object
      properties:
        tenant_id:
          type: string
        active:
          type: boolean
        activated_at:
          type: string
          format: date-time
          nullable: true
        activated_by:
          type: string
          nullable: true
        reason:
          type: string
          nullable: true
        history:
          type: array
          items:
            type: object
            properties:
              activatedAt:
                type: string
                format: date-time
                nullable: true
              deactivatedAt:
                type: string
                format: date-time
                nullable: true
              activatedBy:
                type: string
                nullable: true
              reason:
                type: string
                nullable: true
    ForensicSnapshot:
      type: object
      required: [id, agentId, anomalyScore, severity, violatedRules, blastRadius, activeConnections, createdAt, details, _raw]
      properties:
        id:
          type: string
        agentId:
          type: string
        spiffeId:
          type: string
        anomalyScore:
          type: number
          minimum: 0
          maximum: 1
          description: "Anomaly score between 0.0 (normal) and 1.0 (extreme anomaly)"
        severity:
          type: string
          enum: [low, medium, high, critical]
        violatedRules:
          type: array
          items:
            type: string
        blastRadius:
          type: object
          properties:
            agentCount:
              type: integer
            impactScore:
              type: number
        activeConnections:
          type: object
          properties:
            upstream:
              type: array
              items:
                type: string
            downstream:
              type: array
              items:
                type: string
        createdAt:
          type: string
          format: date-time
        details:
          nullable: true
          type: object
          properties:
            triggeringPayload:
              description: "The payload that triggered the agent termination"
            principalClaims:
              description: "JWT claims of the agent principal at time of termination"
        _raw:
          type: object
          additionalProperties: true
          description: "Complete stored snapshot for lossless case archival"

    ForensicSnapshotRaw:
      type: object
      description: "Raw forensic snapshot as stored. Returned by /v1/mesh/forensics (API key auth)."
      required: [id, spiffeId, anomalyScore, severity, createdAt]
      properties:
        id:
          type: string
        tenantId:
          type: string
        spiffeId:
          type: string
        hostname:
          type: string
        anomalyScore:
          type: number
        severity:
          type: string
          enum: [low, medium, high, critical]
        principalClaims:
          type: object
          additionalProperties: true
        triggeringPayload:
          type: object
          additionalProperties: true
        activeConnections:
          type: object
          properties:
            upstream:
              type: array
              items:
                type: string
            downstream:
              type: array
              items:
                type: string
        violatedRules:
          type: array
          items:
            type: string
        blastRadius:
          type: object
          properties:
            agentIds:
              type: array
              items:
                type: string
            estimatedImpact:
              type: string
        revokedSerial:
          type: string
        createdAt:
          type: string
          format: date-time

    HealthResponse:
      type: object
      properties:
        status:
          type: string
          enum: [ok]
        version:
          type: string
        timestamp:
          type: string
          format: date-time

    MemoryEntry:
      type: object
      properties:
        id:
          type: string
        fact:
          type: string
        block:
          type: string
          enum: [human, project, system, general]
        pinned:
          type: boolean
        created_at:
          type: string
          format: date-time

    MemoryBlock:
      type: object
      properties:
        name:
          type: string
          enum: [human, project, system, general]
        count:
          type: integer
        limit:
          type: integer
        pinned_count:
          type: integer

    GuardrailProvider:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        type:
          type: string
          enum: [builtin, external]
        description:
          type: string
        endpoint:
          type: string

    GuardrailConfig:
      type: object
      properties:
        enabled:
          type: boolean
        mode:
          type: string
          enum: [block, warn, log]
        confidenceThreshold:
          type: number
        providers:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              enabled:
                type: boolean

    Prompt:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        latest_version:
          type: integer
        environment:
          type: string
          enum: [dev, staging, prod]
        versions_count:
          type: integer
        ab_test_active:
          type: boolean

    PromptVersion:
      type: object
      properties:
        version:
          type: integer
        template:
          type: string
        defaults:
          type: object
          additionalProperties:
            type: string
        environment:
          type: string
          enum: [dev, staging, prod]
        created_by:
          type: string
        created_at:
          type: string
          format: date-time
        change_note:
          type: string

    GuardrailTemplate:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        defaultEndpoint:
          type: string
        description:
          type: string

    GuardrailTestResult:
      type: object
      properties:
        result:
          type: object
          properties:
            safe:
              type: boolean
            matches:
              type: array
              items:
                type: object
            scannersRun:
              type: integer
            shortCircuitedBy:
              type: string
            durationMs:
              type: number
        providers:
          type: object
          properties:
            resolved:
              type: array
              items:
                type: string
            failed:
              type: array
              items:
                type: string

    RoutingPreset:
      type: object
      properties:
        slug:
          type: string
        name:
          type: string
        description:
          type: string
        model:
          type: string
        strategy:
          type: string
          enum: [price, latency, throughput, priority, quality, cascade]
        fallbacks:
          type: array
          items:
            type: string
        provider_allow:
          type: array
          items:
            type: string
        provider_deny:
          type: array
          items:
            type: string
        max_cost_usd:
          type: number
        variant:
          type: string
        usage:
          type: string
          description: 'Usage reference, e.g. "@preset/my-config"'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    ObservabilityDestination:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        type:
          type: string
          enum: [webhook, otlp, datadog, splunk, posthog, s3, custom]
        enabled:
          type: boolean
        eventTypes:
          type: array
          items:
            type: string
            enum: [usage, audit, quality, guardrail, error]
        config:
          type: object
          description: Type-specific config (secrets redacted in responses)
        batch:
          type: object
          properties:
            maxSize:
              type: integer
            flushIntervalMs:
              type: integer

    GovernanceSummary:
      type: object
      properties:
        memory:
          type: object
          properties:
            total_entries:
              type: integer
            pinned_entries:
              type: integer
            blocks:
              type: array
              items:
                type: object
                properties:
                  block:
                    type: string
                  entries:
                    type: integer
                  limit:
                    type: integer
                  utilization:
                    type: integer
                    description: Percent 0-100
            pii_violations:
              type: integer
            compliant:
              type: boolean
        activity_24h:
          type: object
          additionalProperties:
            type: integer

    AuditEntry:
      type: object
      properties:
        id:
          type: string
        timestamp:
          type: string
          format: date-time
        operation:
          type: string
        block:
          type: string
        entry_id:
          type: string
        fact_before:
          type: [string, "null"]
        fact_after:
          type: [string, "null"]
        actor:
          type: string
        reason:
          type: [string, "null"]

    LeaderboardEntry:
      type: object
      properties:
        id:
          type: string
        provider:
          type: string
        model_id:
          type: string
        reward_score:
          type: number
        validity:
          type: number
        quality:
          type: [number, "null"]
        latency_ms:
          type: [number, "null"]
        tokens_per_second:
          type: [number, "null"]
        success_rate:
          type: [number, "null"]
        pricing:
          type: [object, "null"]
          properties:
            input:
              type: number
            output:
              type: number
        capabilities:
          type: array
          items:
            type: string
        sample_count:
          type: integer

    OpsStatus:
      type: object
      properties:
        status:
          type: string
        timestamp:
          type: string
          format: date-time
        endpoints:
          type: object
          properties:
            total:
              type: integer
            healthy:
              type: integer
            degraded:
              type: integer
            unhealthy:
              type: integer
        budget:
          type: object
          properties:
            globalSpendTodayUsd:
              type: number
            remainingGlobalUsd:
              type: [number, "null"]
        usage:
          type: [object, "null"]
          properties:
            todayRequests:
              type: integer
            todayCostUsd:
              type: number
            todayAvgLatencyMs:
              type: number

    Task:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        enabled:
          type: boolean
        schedule:
          type: object
          properties:
            type:
              type: string
            expression:
              type: string
            timezone:
              type: string
        prompt:
          type: string
        model:
          type: string
        conversation_id:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    ConfigSection:
      type: object
      properties:
        data:
          type: object
        version:
          type: integer
        updatedAt:
          type: string
          format: date-time

    # --- Batch D schemas ---

    AgentProfile:
      type: object
      properties:
        id:
          type: string
          format: uuid
        tenantId:
          type: string
        agentId:
          type: string
        displayName:
          type: [string, "null"]
        ownerId:
          type: [string, "null"]
        costCenter:
          type: [string, "null"]
        budgetDailyUsd:
          type: [number, "null"]
        budgetMonthlyUsd:
          type: [number, "null"]
        role:
          type: string
        lifecycleState:
          type: string
          enum: [provisioned, active, quarantined, suspended, terminated]
        manifestId:
          type: [string, "null"]
        metadata:
          type: object
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    AgentProfileInput:
      type: object
      required: [agentId]
      properties:
        agentId:
          type: string
        displayName:
          type: string
        ownerId:
          type: string
        costCenter:
          type: string
        budgetDailyUsd:
          type: number
        budgetMonthlyUsd:
          type: number
        role:
          type: string
        manifestId:
          type: string
        metadata:
          type: object

    AgentCertResponse:
      type: object
      properties:
        cert_pem:
          type: string
          description: PEM-encoded signed client certificate
        serial_number:
          type: string
        expires_at:
          type: string
          format: date-time
        spiffe_id:
          type: string
        ca_cert_pem:
          type: string
          description: PEM-encoded CA certificate

    AgentBootstrapResponse:
      type: object
      properties:
        profile:
          $ref: "#/components/schemas/AgentProfile"
        jwt:
          type: string
          description: 1-hour agent JWT for authenticated requests
        jwt_expires_at:
          type: string
          format: date-time
        budget:
          type: object
          properties:
            profile_daily_usd:
              type: number
              nullable: true
            profile_monthly_usd:
              type: number
              nullable: true
            enforced_daily_usd:
              type: number
              nullable: true
            enforced_rpm:
              type: number
              nullable: true
        next_steps:
          type: object
          properties:
            cert_exchange:
              type: string
            self_status:
              type: string

    AgentLimitsStatus:
      type: object
      properties:
        profile_daily_usd:
          type: number
          nullable: true
        profile_monthly_usd:
          type: number
          nullable: true
        enforced_daily_usd:
          type: number
          nullable: true
        enforced_daily_spent_usd:
          type: number
        enforced_daily_remaining_usd:
          type: number
          nullable: true
        enforced_rpm:
          type: number
          nullable: true
        requests_this_minute:
          type: integer

    AgentLimitEntry:
      type: object
      properties:
        maxBudgetUsd:
          type: number
          description: Daily budget cap in USD
        maxRequestsPerMinute:
          type: number
          description: Rate limit in requests per minute

    MeshAgent:
      type: object
      properties:
        id:
          type: string
          format: uuid
        tenantId:
          type: string
        spiffeId:
          type: string
        hostname:
          type: string
        endpoint:
          type: string
        capabilities:
          type: array
          items:
            type: string
        lastHeartbeat:
          type: string
          format: date-time
        status:
          type: string
          enum: [online, offline, degraded]

    MeshPolicy:
      type: object
      properties:
        id:
          type: string
          format: uuid
        sourcePattern:
          type: string
        destinationPattern:
          type: string
        effect:
          type: string
          enum: [allow, deny]
        priority:
          type: integer
        description:
          type: string
        createdBy:
          type: string
        createdAt:
          type: string
          format: date-time

    BehavioralProfile:
      type: object
      properties:
        id:
          type: string
          format: uuid
        role:
          type: string
        maxRps:
          type: number
        maxPayloadBytes:
          type: integer
        allowedDestinations:
          type: array
          items:
            type: string
        maxFanOut:
          type: integer
        maxRequestsPerWindow:
          type: integer
        windowSeconds:
          type: integer
        createdAt:
          type: string
          format: date-time

    A2APayload:
      type: object
      required: [destination, messages]
      properties:
        destination:
          type: string
          description: Destination agent hostname
        messages:
          type: array
          items:
            type: object
            properties:
              role:
                type: string
                enum: [system, user, assistant, tool]
              content:
                type: string
              name:
                type: string
              toolCallId:
                type: string
        toolCalls:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              type:
                type: string
                enum: [function]
              function:
                type: object
                properties:
                  name:
                    type: string
                  arguments:
                    type: string
        metadata:
          type: object
          properties:
            maxHops:
              type: integer
            custom:
              type: object

    ScimUser:
      type: object
      properties:
        schemas:
          type: array
          items:
            type: string
        id:
          type: string
          format: uuid
        externalId:
          type: string
        userName:
          type: string
        displayName:
          type: string
        active:
          type: boolean
        emails:
          type: array
          items:
            type: object
            properties:
              value:
                type: string
                format: email
              primary:
                type: boolean
        groups:
          type: array
          items:
            type: object
            properties:
              value:
                type: string
              display:
                type: string
        meta:
          type: object
          properties:
            resourceType:
              type: string
            created:
              type: string
              format: date-time
            lastModified:
              type: string
              format: date-time

    ScimGroup:
      type: object
      properties:
        schemas:
          type: array
          items:
            type: string
        id:
          type: string
          format: uuid
        externalId:
          type: string
        displayName:
          type: string
        members:
          type: array
          items:
            type: object
            properties:
              value:
                type: string
              display:
                type: string
        meta:
          type: object
          properties:
            resourceType:
              type: string
            created:
              type: string
              format: date-time
            lastModified:
              type: string
              format: date-time

    ScimListResponse:
      type: object
      properties:
        schemas:
          type: array
          items:
            type: string
        totalResults:
          type: integer
        startIndex:
          type: integer
        itemsPerPage:
          type: integer
        Resources:
          type: array
          items:
            type: object

    ScimError:
      type: object
      properties:
        schemas:
          type: array
          items:
            type: string
        detail:
          type: string
        status:
          type: integer

    ScimPatchOp:
      type: object
      properties:
        Operations:
          type: array
          items:
            type: object
            properties:
              op:
                type: string
                enum: [replace, add, remove]
              path:
                type: string
              value: {}

    # ── Admin Schemas ──────────────────────────────────────────────────

    TenantSummary:
      type: object
      properties:
        id:
          type: string
          format: uuid
        slug:
          type: string
        name:
          type: string
        plan:
          type: string
        status:
          type: string
          enum: [pending, active, suspended, rejected]
        createdAt:
          type: string
          format: date-time

    TenantDetail:
      type: object
      description: Full tenant record including all fields

    AdminActionResult:
      type: object
      properties:
        status:
          type: string
          description: "Resulting status (e.g. approved, rejected, suspended)"
        tenant_id:
          type: string
          format: uuid

    DryRunResult:
      type: object
      properties:
        dry_run:
          type: boolean
          const: true
        would_execute:
          type: object
          description: Projected action details

    InviteCode:
      type: object
      properties:
        id:
          type: string
          format: uuid
        code:
          type: string
        maxUses:
          type: integer
          nullable: true
        usedCount:
          type: integer
        createdBy:
          type: string
          nullable: true
        expiresAt:
          type: string
          format: date-time
          nullable: true
        createdAt:
          type: string
          format: date-time

    BusinessDigest:
      type: object
      properties:
        tenants:
          type: object
          additionalProperties:
            type: integer
          description: Tenant count by status
        total_tenants:
          type: integer
        pending_approvals:
          type: integer
        total_spend_30d_usd:
          type: number
        timestamp:
          type: string
          format: date-time

    ClaimLinkResponse:
      type: object
      required: [token, url, expires_at, role, max_uses]
      properties:
        token:
          type: string
          description: "Claim token (brct_... prefix). Show once."
        url:
          type: string
          description: "Dashboard URL with claim token embedded."
        expires_at:
          type: string
          format: date-time
        role:
          type: string
          enum: [owner, admin, member]
        max_uses:
          type: integer

    ClaimResult:
      type: object
      required: [tenant, role]
      properties:
        tenant:
          type: object
          properties:
            id:
              type: string
              format: uuid
            slug:
              type: string
            name:
              type: string
        role:
          type: string
          enum: [owner, admin, member]
        memberships:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
                format: uuid
              slug:
                type: string
              name:
                type: string
              role:
                type: string

    DeviceAuthorizeResponse:
      type: object
      required: [device_code, user_code, verification_uri, expires_in, interval]
      properties:
        device_code:
          type: string
          description: "Opaque device code for polling."
        user_code:
          type: string
          description: "Human-readable code (BRSR-XXXXXX) for browser entry."
        verification_uri:
          type: string
        expires_in:
          type: integer
          description: "Seconds until expiry."
        interval:
          type: integer
          description: "Recommended polling interval in seconds."

paths:
  /health:
    get:
      operationId: getHealth
      summary: Health check
      security: []
      responses:
        "200":
          description: Service is healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthResponse"
        "503":
          description: Service unhealthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/register:
    post:
      operationId: registerTenant
      summary: Machine registration — zero-browser tenant onboarding
      description: |
        Create a new tenant, admin user, and API key in a single atomic
        transaction. No authentication required — the invite code is the
        trust anchor.

        Machine-registered tenants are API-only. Dashboard access requires
        a separate browser-based signup.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [invite_code, tenant_name, admin_email, accept_tos]
              properties:
                invite_code:
                  type: string
                  description: Invite code for tenant creation.
                tenant_name:
                  type: string
                  description: Display name for the new tenant.
                admin_email:
                  type: string
                  format: email
                  description: Admin email for billing and notifications.
                accept_tos:
                  type: boolean
                  description: Must be true — legal gate.
                  enum: [true]
                tenant_slug:
                  type: string
                  description: Custom slug (auto-generated from tenant_name if omitted).
                key_name:
                  type: string
                  description: Name for the auto-created API key (default "Auto-provisioned").
      responses:
        "201":
          description: Tenant registered successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  tenant:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      slug:
                        type: string
                      name:
                        type: string
                  api_key:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      key:
                        type: string
                        description: Plaintext API key — shown once, never again.
                      name:
                        type: string
                      scopes:
                        type: array
                        items:
                          type: string
                  next_steps:
                    type: object
                    properties:
                      bootstrap_agent:
                        type: string
                      register_provider:
                        type: string
        "400":
          description: Validation error or invalid/expired invite code
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Registration rate limit exceeded (5 per hour per IP)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/chat/completions:
    post:
      operationId: createChatCompletion
      summary: Create a chat completion
      description: |
        OpenAI-compatible chat completions endpoint with intelligent routing.
        Use `model: "auto"` for automatic model selection based on request complexity.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChatCompletionRequest"
      responses:
        "200":
          description: Completion response
          headers:
            X-BR-Guardian-Status:
              schema:
                type: string
              description: Guardian processing status
            X-BR-Estimated-Cost:
              schema:
                type: string
              description: Estimated cost in USD
            X-BR-Actual-Cost:
              schema:
                type: string
              description: Actual cost in USD
            X-BR-Selection-Method:
              schema:
                type: string
                enum: [thompson-sampling, complexity-based, cheapest-fallback, conversation-downgrade, history-based, explicit]
              description: Which intelligence strategy selected the model
            X-BR-Complexity-Level:
              schema:
                type: string
                enum: [simple, moderate, complex]
              description: Assessed complexity level of the request
            X-BR-Complexity-Score:
              schema:
                type: string
              description: Raw complexity score (0-10)
            X-BR-Selection-Confidence:
              schema:
                type: string
              description: Confidence in model selection (0.00-1.00)
            X-BR-Models-Considered:
              schema:
                type: string
              description: Number of eligible models considered
            X-BR-Budget-Remaining:
              schema:
                type: string
              description: Remaining budget in USD
            X-BR-Memory-Facts-Injected:
              schema:
                type: string
              description: Number of memory facts injected into context
            X-BR-Context:
              schema:
                type: string
              description: JSON composite of all intelligence signals
            X-BR-Routed-Model:
              schema:
                type: string
              description: The provider/model actually used (e.g. "anthropic/claude-sonnet-4")
            X-BR-Route-Reason:
              schema:
                type: string
              description: Why this specific model was chosen
            X-BR-Route-Confidence:
              schema:
                type: string
              description: Bandit or heuristic confidence score
            X-BR-Cache:
              schema:
                type: string
                enum: [hit]
              description: Present with value "hit" when semantic cache served the response
            X-BR-Total-Latency-Ms:
              schema:
                type: string
              description: End-to-end request time in milliseconds
            X-BR-Provider-Latency-Ms:
              schema:
                type: string
              description: Upstream provider latency only (excludes BR overhead)
            X-BR-Routing-Overhead-Ms:
              schema:
                type: string
              description: BrainstormRouter processing overhead in milliseconds
            X-BR-Failover-From:
              schema:
                type: string
              description: Original model when circuit breaker/fallback rerouted
            X-BR-Router-Recovery:
              schema:
                type: string
                enum: ["true"]
              description: Present when auto-recovery redirected the request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChatCompletionResponse"
            text/event-stream:
              schema:
                type: string
                description: SSE stream of completion chunks
        "400":
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "402":
          description: Budget exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/messages:
    post:
      operationId: createMessage
      summary: Create a message (Anthropic Messages API)
      description: |
        Anthropic Messages API-compatible endpoint. Accepts requests in Anthropic
        format and returns responses in Anthropic format, while running through
        the same routing, budget, guardian, guardrails, and audit pipeline as
        /v1/chat/completions. Supports both streaming and non-streaming modes.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [model, max_tokens, messages]
              properties:
                model:
                  type: string
                  description: Model to use (e.g. "claude-sonnet-4-20250514" or "auto")
                max_tokens:
                  type: integer
                  description: Maximum tokens to generate
                messages:
                  type: array
                  items:
                    type: object
                system:
                  oneOf:
                    - type: string
                    - type: array
                  description: System prompt (string or content blocks)
                stream:
                  type: boolean
                  description: Whether to stream the response
                tools:
                  type: array
                  items:
                    type: object
                temperature:
                  type: number
                top_p:
                  type: number
      responses:
        "200":
          description: Message response in Anthropic format
          headers:
            X-BR-Guardian-Status:
              schema:
                type: string
            X-BR-Estimated-Cost:
              schema:
                type: string
              description: Estimated cost in USD
            X-BR-Actual-Cost:
              schema:
                type: string
              description: Actual cost in USD
            X-BR-Selection-Method:
              schema:
                type: string
                enum: [thompson-sampling, complexity-based, cheapest-fallback, conversation-downgrade, history-based, explicit]
              description: Which intelligence strategy selected the model
            X-BR-Complexity-Level:
              schema:
                type: string
                enum: [simple, moderate, complex]
              description: Assessed complexity level of the request
            X-BR-Complexity-Score:
              schema:
                type: string
              description: Raw complexity score (0-10)
            X-BR-Selection-Confidence:
              schema:
                type: string
              description: Confidence in model selection (0.00-1.00)
            X-BR-Models-Considered:
              schema:
                type: string
              description: Number of eligible models considered
            X-BR-Budget-Remaining:
              schema:
                type: string
              description: Remaining budget in USD
            X-BR-Memory-Facts-Injected:
              schema:
                type: string
              description: Number of memory facts injected into context
            X-BR-Routed-Model:
              schema:
                type: string
              description: The provider/model actually used
            X-BR-Route-Reason:
              schema:
                type: string
              description: Why this specific model was chosen
            X-BR-Cache:
              schema:
                type: string
                enum: [hit]
              description: Present with value "hit" when semantic cache served the response
            X-BR-Total-Latency-Ms:
              schema:
                type: string
              description: End-to-end request time in milliseconds
            X-BR-Provider-Latency-Ms:
              schema:
                type: string
              description: Upstream provider latency only
            X-BR-Routing-Overhead-Ms:
              schema:
                type: string
              description: BrainstormRouter processing overhead in milliseconds
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  type:
                    type: string
                    enum: [message]
                  role:
                    type: string
                    enum: [assistant]
                  model:
                    type: string
                  stop_reason:
                    type: string
                  content:
                    type: array
                  usage:
                    type: object
            text/event-stream:
              schema:
                type: string
                description: Anthropic-format SSE stream
        "400":
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/models:
    get:
      operationId: listModels
      summary: List available models
      parameters:
        - name: includeUnhealthy
          in: query
          schema:
            type: string
            enum: ["true", "1"]
          description: Include models with open circuit breakers
      responses:
        "200":
          description: Model list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ModelList"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/discovery:
    get:
      operationId: getDiscovery
      summary: Capability discovery
      description: |
        Returns everything an AI agent needs to know about BrainstormRouter's
        capabilities, routing strategies, health, budget, and available models.
        Call this first after authenticating to understand the system.
      tags: [MNI]
      responses:
        "200":
          description: Full capability discovery response
          content:
            application/json:
              schema:
                type: object
                properties:
                  capabilities:
                    type: object
                    description: Routing strategies, memory, guardrails, MCP, governance
                    properties:
                      routing:
                        type: object
                        properties:
                          strategies:
                            type: array
                            items:
                              type: string
                          auto_variants:
                            type: object
                            description: "auto, auto:fast, auto:floor, auto:best"
                          auto_selector:
                            type: object
                      memory:
                        type: object
                        properties:
                          enabled:
                            type: boolean
                          blocks:
                            type: array
                            items:
                              type: string
                            description: "human, system, project, general"
                      guardrails:
                        type: object
                      mcp:
                        type: object
                      governance:
                        type: object
                  budget:
                    type: object
                    nullable: true
                    description: Daily and monthly spend vs limits
                  health:
                    type: object
                    description: Per-provider health, circuit breaker counts, kill switch state
                  models:
                    type: object
                    description: Total models, endpoints, per-provider counts
                  intelligence_headers:
                    type: object
                    description: Documentation of X-BR-* response headers
                  error_recovery:
                    type: object
                    description: All error types and recovery actions
                  endpoints:
                    type: object
                    description: Key API endpoint paths

  /v1/self:
    get:
      operationId: getSelf
      summary: Agent self-awareness megaendpoint
      description: |
        Single call returns everything an agent needs: identity, capabilities,
        health (sentinel + circuit breaker), budget, memory, configuration,
        dynamic suggestions, and recent errors. Cached 30s per tenant.
      tags: [MNI]
      responses:
        "200":
          description: Full self-awareness response
          content:
            application/json:
              schema:
                type: object
                properties:
                  identity:
                    type: object
                    properties:
                      tenant_id:
                        type: string
                      auth_method:
                        type: string
                      key_id:
                        type: string
                        nullable: true
                      agent_id:
                        type: string
                        nullable: true
                      roles:
                        type: array
                        items:
                          type: string
                      scopes:
                        type: array
                        items:
                          type: string
                  capabilities:
                    type: object
                    properties:
                      granted:
                        type: array
                        items:
                          type: string
                      role_hierarchy:
                        type: array
                        items:
                          type: string
                  health:
                    type: object
                    properties:
                      providers:
                        type: object
                        additionalProperties:
                          type: object
                          properties:
                            status:
                              type: string
                            latency_ms:
                              type: number
                            last_probe_at:
                              type: string
                      circuit_breakers:
                        type: object
                        properties:
                          total:
                            type: integer
                          open:
                            type: integer
                          closed:
                            type: integer
                  budget:
                    type: object
                    nullable: true
                    properties:
                      daily:
                        type: object
                        properties:
                          spent_usd:
                            type: number
                          limit_usd:
                            type: number
                            nullable: true
                          remaining_usd:
                            type: number
                            nullable: true
                      monthly:
                        type: object
                        properties:
                          spent_usd:
                            type: number
                          limit_usd:
                            type: number
                            nullable: true
                          remaining_usd:
                            type: number
                            nullable: true
                  memory:
                    type: object
                    nullable: true
                    properties:
                      total_entries:
                        type: integer
                      blocks:
                        type: object
                        additionalProperties:
                          type: integer
                      pinned:
                        type: integer
                  configuration:
                    type: object
                    properties:
                      guardrails:
                        type: object
                        properties:
                          enabled:
                            type: boolean
                          mode:
                            type: string
                      guardian:
                        type: object
                        properties:
                          mode:
                            type: string
                      kill_switch:
                        type: object
                        properties:
                          active:
                            type: boolean
                          reason:
                            type: string
                            nullable: true
                      privacy_mode:
                        type: string
                  suggestions:
                    type: array
                    items:
                      type: object
                      properties:
                        action:
                          type: string
                        reason:
                          type: string
                        endpoint:
                          type: string
                  recent_errors:
                    type: array
                    items:
                      type: object
                  models_available:
                    type: integer
                  endpoints:
                    type: object
                    additionalProperties:
                      type: string
        "401":
          description: Missing or invalid API key

  /v1/catalog/runnable:
    get:
      operationId: listRunnableModels
      summary: Models that will actually work right now
      description: |
        Filters the full model list by circuit breaker state, sentinel health,
        BYOK key availability, and budget. Returns only models guaranteed to
        succeed, with counts of why others were filtered out.
      tags: [MNI]
      responses:
        "200":
          description: Runnable model list with filter breakdown
          content:
            application/json:
              schema:
                type: object
                properties:
                  object:
                    type: string
                    enum: [list]
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Model"
                  filtered_out:
                    type: object
                    properties:
                      circuit_open:
                        type: integer
                      no_provider_key:
                        type: integer
                      sentinel_down:
                        type: integer
                      budget_exhausted:
                        type: integer
                  total_available:
                    type: integer
                  total_filtered:
                    type: integer
        "401":
          description: Missing or invalid API key

  /v1/health/providers:
    get:
      operationId: getProviderHealth
      summary: Transparent provider health matrix
      description: |
        Per-provider breakdown combining sentinel probes, circuit breaker state,
        and BYOK key status. Shows exactly what's healthy, degraded, or down.
      tags: [MNI]
      responses:
        "200":
          description: Provider health breakdown
          content:
            application/json:
              schema:
                type: object
                properties:
                  providers:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        sentinel:
                          type: object
                          properties:
                            status:
                              type: string
                            latency_ms:
                              type: number
                              nullable: true
                            last_probe_at:
                              type: string
                              nullable: true
                        models:
                          type: array
                          items:
                            type: object
                            properties:
                              id:
                                type: string
                              circuit:
                                type: string
                                enum: [closed, open]
                              routable:
                                type: boolean
                        aggregate:
                          type: object
                          properties:
                            total_models:
                              type: integer
                            routable_models:
                              type: integer
                            circuit_open_count:
                              type: integer
                            health:
                              type: string
                              enum: [healthy, degraded, down]
                        has_key:
                          type: boolean
                  summary:
                    type: object
                    properties:
                      total_providers:
                        type: integer
                      healthy:
                        type: integer
                      degraded:
                        type: integer
                      down:
                        type: integer
        "401":
          description: Missing or invalid API key

  /v1/aliases:
    get:
      operationId: getAliases
      summary: Get tenant model aliases
      responses:
        "200":
          description: Current aliases
          content:
            application/json:
              schema:
                type: object
                properties:
                  aliases:
                    $ref: "#/components/schemas/AliasMap"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    put:
      operationId: setAliases
      summary: Replace all aliases
      description: Requires `config.write` permission (admin or operator role).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AliasMap"
      responses:
        "200":
          description: Aliases updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  aliases:
                    $ref: "#/components/schemas/AliasMap"
                  version:
                    type: integer
        "400":
          description: "Invalid body — must be flat { alias: provider/model }"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: API key lacks `config.write` permission
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    patch:
      operationId: patchAliases
      summary: Merge aliases (set null to delete)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties:
                oneOf:
                  - type: string
                  - type: "null"
      responses:
        "200":
          description: Aliases updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  aliases:
                    $ref: "#/components/schemas/AliasMap"
                  version:
                    type: integer
        "400":
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ── Budget ───────────────────────────────────────────────────────────

  /v1/budget/status:
    get:
      operationId: getBudgetStatus
      summary: Get budget status
      description: Returns current spend, limits, and remaining budget for the tenant and API key.
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Budget status
          content:
            application/json:
              schema:
                type: object
                properties:
                  tenant_id:
                    type: string
                  key_id:
                    type: string
                    nullable: true
                  daily:
                    $ref: "#/components/schemas/BudgetPeriodStatus"
                  monthly:
                    $ref: "#/components/schemas/BudgetPeriodStatus"
                  key_budget:
                    nullable: true
                    type: object
                    properties:
                      limit_usd:
                        type: number
                      period:
                        type: string
                        nullable: true
                      spent_usd:
                        type: number
                      remaining_usd:
                        type: number

  /v1/budget/limits:
    put:
      operationId: updateBudgetLimits
      summary: Update budget limits
      description: Set or clear tenant-level daily and monthly spend limits. Pass null to remove a limit.
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                daily_limit_usd:
                  type: number
                  nullable: true
                  description: Daily spend cap in USD (null to clear)
                monthly_limit_usd:
                  type: number
                  nullable: true
                  description: Monthly spend cap in USD (null to clear)
      responses:
        "200":
          description: Limits updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  limits:
                    type: object
                    properties:
                      dailyLimitUsd:
                        type: number
                        nullable: true
                      monthlyLimitUsd:
                        type: number
                        nullable: true
        "400":
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/budget/forecast:
    get:
      operationId: getBudgetForecast
      summary: Get budget forecast
      description: Projects spend based on current velocity and returns depletion estimates.
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Spend forecast
          content:
            application/json:
              schema:
                type: object
                properties:
                  tenant_id:
                    type: string
                  daily:
                    $ref: "#/components/schemas/BudgetForecastPeriod"
                  monthly:
                    $ref: "#/components/schemas/BudgetForecastPeriod"

  # ── Kill Switch ─────────────────────────────────────────────────────

  /v1/killswitch/activate:
    post:
      operationId: activateKillSwitch
      summary: Activate kill switch
      description: Immediately suspends all routing for the tenant. Returns 409 if already active.
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reason]
              properties:
                reason:
                  type: string
                  description: Human-readable activation reason
      responses:
        "200":
          description: Kill switch activated
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  active:
                    type: boolean
                  activated_at:
                    type: string
                    format: date-time
                  activated_by:
                    type: string
                  reason:
                    type: string
        "409":
          description: Kill switch already active
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/killswitch/deactivate:
    post:
      operationId: deactivateKillSwitch
      summary: Deactivate kill switch
      description: Resumes routing for the tenant. Returns 409 if not active.
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Kill switch deactivated
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  active:
                    type: boolean
                  deactivated_at:
                    type: string
                    format: date-time
        "409":
          description: Kill switch not active
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/killswitch/status:
    get:
      operationId: getKillSwitchStatus
      summary: Get kill switch status
      description: Returns current kill switch state and activation history (last 50 events).
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Kill switch state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KillSwitchStatus"

  # ── Dashboard Bridge (JWT auth) ────────────────────────────────────

  /auth/budget/status:
    get:
      operationId: getDashboardBudgetStatus
      summary: Get budget status (dashboard)
      description: |
        JWT-authenticated dashboard endpoint for tenant-level budget status.
        Returns tenant spend and limits (daily/monthly). Unlike `/v1/budget/status`,
        this endpoint has no API key context — `key_id` is always `null` and
        `key_budget` is always `null`. Per-key budget data is only available
        through the API-key-authenticated `/v1/budget/status` endpoint.
      security:
        - SupabaseJWT: []
      responses:
        "200":
          description: Budget status for the tenant
          content:
            application/json:
              schema:
                type: object
                properties:
                  tenant_id:
                    type: string
                  key_id:
                    type: string
                    nullable: true
                    description: Always null — JWT auth has no API key context
                  daily:
                    $ref: "#/components/schemas/BudgetPeriodStatus"
                  monthly:
                    $ref: "#/components/schemas/BudgetPeriodStatus"
                  key_budget:
                    nullable: true
                    description: Always null — per-key budget requires API key auth via `/v1/budget/status`
        "404":
          description: Tenant not provisioned

  /auth/killswitch/status:
    get:
      operationId: getDashboardKillSwitchStatus
      summary: Get kill switch status (dashboard)
      description: |
        JWT-authenticated dashboard endpoint for kill switch status.
        Returns the same response shape as `/v1/killswitch/status`, scoped
        to the authenticated tenant.
      security:
        - SupabaseJWT: []
      responses:
        "200":
          description: Kill switch state for the tenant
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/KillSwitchStatus"
        "404":
          description: Tenant not provisioned

  # ── Usage ───────────────────────────────────────────────────────────

  /v1/usage/summary:
    get:
      operationId: getUsageSummary
      summary: Get usage summary
      parameters:
        - name: period
          in: query
          schema:
            type: string
            enum: [day, week, month]
            default: month
        - name: groupBy
          in: query
          schema:
            type: string
            enum: [model, provider, user]
      responses:
        "200":
          description: Usage data
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UsageSummary"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/providers:
    get:
      operationId: listProviders
      summary: List configured providers
      responses:
        "200":
          description: Provider list
          content:
            application/json:
              schema:
                type: object
                properties:
                  providers:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        provider:
                          type: string
                        masked_key:
                          type: string
                        status:
                          type: string
                        created_at:
                          type: string
                          format: date-time
                  total:
                    type: integer
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: addProvider
      summary: Add a provider API key
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [provider, api_key]
              properties:
                provider:
                  type: string
                  examples: [anthropic, openai, google]
                api_key:
                  type: string
                  description: Plaintext key — encrypted before storage
      responses:
        "201":
          description: Provider added
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  provider:
                    type: string
                  name:
                    type: string
                  masked_key:
                    type: string
                  status:
                    type: string
        "400":
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/providers/batch:
    post:
      operationId: batchAddProviders
      summary: Register multiple provider keys at once
      description: |
        Batch registration with optional per-key validation. Returns 207 Multi-Status
        with per-provider results. Maximum 10 providers per batch.
        Duplicate providers within a single batch are rejected (400).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [providers]
              properties:
                providers:
                  type: array
                  maxItems: 10
                  items:
                    type: object
                    required: [provider, api_key]
                    properties:
                      provider:
                        type: string
                        examples: [anthropic, openai, google]
                      api_key:
                        type: string
                        description: Plaintext key — encrypted before storage
                      test_first:
                        type: boolean
                        description: Validate key with live request before storing
                        default: false
      responses:
        "207":
          description: Per-provider results
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        provider:
                          type: string
                        status:
                          type: string
                          enum: [stored, test_failed, store_failed]
                        id:
                          type: string
                        masked_key:
                          type: string
                        error:
                          type: string
                  summary:
                    type: object
                    properties:
                      total:
                        type: integer
                      stored:
                        type: integer
                      failed:
                        type: integer
        "400":
          description: Invalid request (missing fields, duplicate providers, unknown provider)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/api-keys:
    get:
      operationId: listApiKeys
      summary: List API keys
      responses:
        "200":
          description: API key list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        name:
                          type: string
                        prefix:
                          type: string
                        keyPreview:
                          type: string
                        environment:
                          type: string
                          enum: [live, test]
                        scopes:
                          type: ["array", "null"]
                          items:
                            type: string
                        createdAt:
                          type: string
                          format: date-time
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: createApiKey
      summary: Create an API key
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  default: Unnamed Key
                prefix:
                  type: string
                  enum: [br_live_, br_test_]
                scopes:
                  type: array
                  items:
                    type: string
                    enum: [admin, operator, developer, auditor, agent]
                allowed_models:
                  type: array
                  items:
                    type: string
                rate_limit_rpm:
                  type: integer
                budget_limit_usd:
                  type: number
                budget_period:
                  type: string
                expires_at:
                  type: string
                  format: date-time
      responses:
        "201":
          description: "Key created — plaintext key shown once"
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  name:
                    type: string
                  key:
                    type: string
                    description: Full key — only returned on creation
                  keyPreview:
                    type: string
                  environment:
                    type: string
                  scopes:
                    type: ["array", "null"]
                    items:
                      type: string
                  createdAt:
                    type: string
                    format: date-time
        "400":
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ─── Memory ───────────────────────────────────────────────────────

  /v1/memory/blocks:
    get:
      operationId: listMemoryBlocks
      summary: List all memory blocks with entry counts
      responses:
        "200":
          description: Memory blocks with counts
          content:
            application/json:
              schema:
                type: object
                properties:
                  blocks:
                    type: array
                    items:
                      $ref: "#/components/schemas/MemoryBlock"
                  total_core_entries:
                    type: integer
                  total_archival_entries:
                    type: integer
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/memory/blocks/{block}:
    get:
      operationId: getMemoryBlock
      summary: List entries in a specific memory block
      parameters:
        - name: block
          in: path
          required: true
          schema:
            type: string
            enum: [human, project, system, general]
      responses:
        "200":
          description: Block entries
          content:
            application/json:
              schema:
                type: object
                properties:
                  block:
                    type: string
                  limit:
                    type: integer
                  entries:
                    type: array
                    items:
                      $ref: "#/components/schemas/MemoryEntry"
        "400":
          description: Invalid block name
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/memory/entries:
    get:
      operationId: listMemoryEntries
      summary: List all core memory entries
      responses:
        "200":
          description: Memory entries
          content:
            application/json:
              schema:
                type: object
                properties:
                  entries:
                    type: array
                    items:
                      $ref: "#/components/schemas/MemoryEntry"
                  total:
                    type: integer
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: createMemoryEntry
      summary: Append a new core memory entry
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [context]
              properties:
                context:
                  type: string
                  description: The fact text to store (also accepts "fact" for legacy compat)
                block:
                  type: string
                  enum: [human, project, system, general]
                  default: general
                pinned:
                  type: boolean
                  default: false
      responses:
        "200":
          description: Entry created
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  id:
                    type: string
                  entry:
                    $ref: "#/components/schemas/MemoryEntry"
        "400":
          description: Missing required field
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/memory/entries/{id}:
    put:
      operationId: updateMemoryEntry
      summary: Update an existing memory entry
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                fact:
                  type: string
                  description: New fact text
                pinned:
                  type: boolean
                  description: Pin or unpin the entry
      responses:
        "200":
          description: Entry updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  entry:
                    $ref: "#/components/schemas/MemoryEntry"
        "404":
          description: Entry not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    delete:
      operationId: deleteMemoryEntry
      summary: Delete a memory entry
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Entry deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  id:
                    type: string
        "404":
          description: Entry not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/memory/init:
    post:
      operationId: initMemory
      summary: Bootstrap memory from context documents
      description: |
        Runs an LLM pass to extract structured facts from documents into memory blocks.
        Admin-only — not available with agent JWT auth.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [documents]
              properties:
                documents:
                  type: array
                  items:
                    type: object
                    required: [content]
                    properties:
                      content:
                        type: string
                      source:
                        type: string
                model:
                  type: string
                  description: Provider/model for extraction (default auto)
      responses:
        "200":
          description: Memory initialized
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  summary:
                    type: string
                  entries_after:
                    type: integer
                  entries:
                    type: array
                    items:
                      $ref: "#/components/schemas/MemoryEntry"
                  usage:
                    type: object
                  duration_ms:
                    type: number
        "400":
          description: Missing or empty documents
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Admin only — agent JWT not allowed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/memory/store:
    post:
      operationId: storeMemory
      summary: Store a memory entry (alias for POST /v1/memory/entries)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [context]
              properties:
                context:
                  type: string
                block:
                  type: string
                  enum: [human, project, system, general]
                  default: general
                pinned:
                  type: boolean
                  default: false
      responses:
        "200":
          description: Entry created
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  id:
                    type: string
                  entry:
                    $ref: "#/components/schemas/MemoryEntry"
        "400":
          description: Missing required field
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/memory/query:
    post:
      operationId: queryMemory
      summary: Search memory entries by relevance
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [query]
              properties:
                query:
                  type: string
                  description: Natural language query to match against memory
                agent_id:
                  type: string
      responses:
        "200":
          description: Ranked memory results
          content:
            application/json:
              schema:
                type: object
                properties:
                  query:
                    type: string
                  agent_id:
                    type: string
                  results:
                    type: array
                    items:
                      allOf:
                        - $ref: "#/components/schemas/MemoryEntry"
                        - type: object
                          properties:
                            context:
                              type: string
                            score:
                              type: number
                            relevance:
                              type: number
        "400":
          description: Missing query field
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ─── Guardrails ───────────────────────────────────────────────────

  /v1/guardrails/providers:
    get:
      operationId: listGuardrailProviders
      summary: List available guardrail providers
      responses:
        "200":
          description: Guardrail providers
          content:
            application/json:
              schema:
                type: object
                properties:
                  providers:
                    type: array
                    items:
                      $ref: "#/components/schemas/GuardrailProvider"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: registerGuardrailProvider
      summary: Register an external guardrail provider
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [id, name]
              properties:
                id:
                  type: string
                name:
                  type: string
                endpoint:
                  type: string
                api_key:
                  type: string
                template:
                  type: string
                  description: Use a pre-configured template (e.g. "openai-moderation")
                auth_header:
                  type: string
                auth_prefix:
                  type: string
                body_template:
                  type: object
                headers:
                  type: object
                  additionalProperties:
                    type: string
                timeout_ms:
                  type: integer
                result_mapping:
                  type: object
                  description: Required when not using a template
      responses:
        "201":
          description: Provider registered
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  name:
                    type: string
                  endpoint:
                    type: string
                  status:
                    type: string
        "400":
          description: Invalid request or reserved ID
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/guardrails/providers/{id}:
    delete:
      operationId: removeGuardrailProvider
      summary: Remove an external guardrail provider
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Provider removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  status:
                    type: string
        "400":
          description: Cannot remove built-in provider
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Provider not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/guardrails/templates:
    get:
      operationId: listGuardrailTemplates
      summary: List well-known guardrail provider templates
      responses:
        "200":
          description: Provider templates
          content:
            application/json:
              schema:
                type: object
                properties:
                  templates:
                    type: array
                    items:
                      $ref: "#/components/schemas/GuardrailTemplate"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/guardrails/config:
    get:
      operationId: getGuardrailConfig
      summary: Get tenant guardrail pipeline configuration
      responses:
        "200":
          description: Guardrail config
          content:
            application/json:
              schema:
                type: object
                properties:
                  config:
                    $ref: "#/components/schemas/GuardrailConfig"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    put:
      operationId: updateGuardrailConfig
      summary: Update tenant guardrail pipeline configuration
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GuardrailConfig"
      responses:
        "200":
          description: Config updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  config:
                    $ref: "#/components/schemas/GuardrailConfig"
                  status:
                    type: string
        "400":
          description: Invalid configuration
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/guardrails/test:
    post:
      operationId: testGuardrails
      summary: Test guardrail pipeline with sample text
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [text]
              properties:
                text:
                  type: string
                  description: Sample text to run through the guardrail pipeline
                providers:
                  type: array
                  items:
                    type: string
                  description: Override which providers to test (defaults to tenant config)
      responses:
        "200":
          description: Test results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GuardrailTestResult"
        "400":
          description: Missing text field
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ─── Prompts ──────────────────────────────────────────────────────

  /v1/prompts:
    post:
      operationId: createPrompt
      summary: Create a prompt template
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [id, template]
              properties:
                id:
                  type: string
                template:
                  type: string
                  description: Prompt template text with {{variable}} placeholders
                variables:
                  type: object
                  additionalProperties:
                    type: string
                  description: Default variable values
                environment:
                  type: string
                  enum: [dev, staging, prod]
                  default: dev
                change_note:
                  type: string
      responses:
        "201":
          description: Prompt created
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  version:
                    type: integer
                  environment:
                    type: string
        "400":
          description: Missing required fields
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Prompt ID already exists
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    get:
      operationId: listPrompts
      summary: List all prompt templates
      responses:
        "200":
          description: Prompt list
          content:
            application/json:
              schema:
                type: object
                properties:
                  prompts:
                    type: array
                    items:
                      $ref: "#/components/schemas/Prompt"
                  count:
                    type: integer
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/prompts/{id}:
    get:
      operationId: getPrompt
      summary: Get prompt with version history
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Prompt detail
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  name:
                    type: string
                  versions:
                    type: array
                    items:
                      $ref: "#/components/schemas/PromptVersion"
                  ab_test:
                    type: object
                    properties:
                      active:
                        type: boolean
                      version_a:
                        type: integer
                      version_b:
                        type: integer
                      split_ratio:
                        type: number
        "404":
          description: Prompt not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    put:
      operationId: updatePrompt
      summary: Update prompt (auto-creates new version)
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [template]
              properties:
                template:
                  type: string
                variables:
                  type: object
                  additionalProperties:
                    type: string
                environment:
                  type: string
                  enum: [dev, staging, prod]
                  default: dev
                change_note:
                  type: string
      responses:
        "200":
          description: New version created
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  version:
                    type: integer
                  environment:
                    type: string
        "400":
          description: Missing template
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Prompt not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    delete:
      operationId: archivePrompt
      summary: Archive a prompt template
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Prompt archived
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  status:
                    type: string
        "404":
          description: Prompt not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/prompts/{id}/test:
    post:
      operationId: testPrompt
      summary: Test-resolve a prompt with variables
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                variables:
                  type: object
                  additionalProperties:
                    type: string
                environment:
                  type: string
                  enum: [dev, staging, prod]
                version:
                  type: integer
      responses:
        "200":
          description: Resolved prompt
          content:
            application/json:
              schema:
                type: object
                properties:
                  resolved_text:
                    type: string
                  template_id:
                    type: string
                  version:
                    type: integer
                  is_ab_test:
                    type: boolean
                  ab_variant:
                    type: string
        "404":
          description: Prompt not found or no matching version
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ─── Insights ─────────────────────────────────────────────────────

  /v1/insights/daily:
    get:
      operationId: getInsightsDaily
      summary: Daily usage insights
      description: Requires sufficient historical usage data to produce results.
      responses:
        "200":
          description: Daily insights
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum: [ready, warming_up, unavailable]
                  totalRequests:
                    type: integer
                  todayRequests:
                    type: integer
                  todayCostUsd:
                    type: number
                  topModels:
                    type: array
                    items:
                      type: object
                  avgLatencyMs:
                    type: number
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "503":
          description: Database not configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/insights/waste:
    get:
      operationId: getInsightsWaste
      summary: Waste detection report
      description: |
        Identifies wasted spend from retries, duplicate requests, and overprovisioned models.
        Requires privacy_mode: standard (not strict).
      responses:
        "200":
          description: Waste report
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  wastedUsd:
                    type: number
                  duplicateRequests:
                    type: integer
                  recommendations:
                    type: array
                    items:
                      type: object
        "403":
          description: Privacy mode restricts waste report
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "503":
          description: Database not configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/insights/optimize:
    get:
      operationId: getInsightsOptimize
      summary: Cost optimization suggestions
      description: Analyzes usage patterns to suggest model swaps and cost savings.
      responses:
        "200":
          description: Optimization suggestions
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum: [ready, warming_up, unavailable]
                  totalRequests:
                    type: integer
                  suggestions:
                    type: array
                    items:
                      type: object
                  potentialSavingsUsd:
                    type: number
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "503":
          description: Database not configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/insights/forecast:
    get:
      operationId: getInsightsForecast
      summary: Budget forecast
      description: Projects future spend based on historical usage trends.
      parameters:
        - name: budget
          in: query
          schema:
            type: number
          description: Budget cap in USD for projection
        - name: period
          in: query
          schema:
            type: string
            enum: [daily, monthly]
            default: monthly
      responses:
        "200":
          description: Forecast data
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum: [ready, warming_up, unavailable]
                  projectedCostUsd:
                    type: number
                  daysUntilBudgetExhausted:
                    type: integer
                  dailyTrend:
                    type: array
                    items:
                      type: object
                      properties:
                        day:
                          type: string
                          format: date
                        daily_cost_usd:
                          type: number
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "503":
          description: Database not configured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ─── Batch B: Presets ─────────────────────────────────────────────────

  /v1/presets:
    get:
      operationId: listPresets
      summary: List routing presets
      description: List all active (non-archived) routing presets.
      responses:
        "200":
          description: Preset list
          content:
            application/json:
              schema:
                type: object
                properties:
                  presets:
                    type: array
                    items:
                      $ref: "#/components/schemas/RoutingPreset"
                  count:
                    type: integer
    post:
      operationId: createPreset
      summary: Create a routing preset
      description: Create a named routing configuration referenced as `@preset/slug`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [slug, name, model]
              properties:
                slug:
                  type: string
                  description: URL-safe identifier
                name:
                  type: string
                model:
                  type: string
                description:
                  type: string
                strategy:
                  type: string
                  enum: [price, latency, throughput, priority, quality, cascade]
                fallbacks:
                  type: array
                  items:
                    type: string
                provider_allow:
                  type: array
                  items:
                    type: string
                provider_deny:
                  type: array
                  items:
                    type: string
                max_cost_usd:
                  type: number
                variant:
                  type: string
      responses:
        "201":
          description: Preset created
          content:
            application/json:
              schema:
                type: object
                properties:
                  preset:
                    $ref: "#/components/schemas/RoutingPreset"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Slug already exists
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/presets/{slug}:
    get:
      operationId: getPreset
      summary: Get a preset
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Preset details
          content:
            application/json:
              schema:
                type: object
                properties:
                  preset:
                    $ref: "#/components/schemas/RoutingPreset"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    put:
      operationId: updatePreset
      summary: Update a preset
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                model:
                  type: string
                strategy:
                  type: string
                fallbacks:
                  type: array
                  items:
                    type: string
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  preset:
                    $ref: "#/components/schemas/RoutingPreset"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    delete:
      operationId: archivePreset
      summary: Archive a preset
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Archived
          content:
            application/json:
              schema:
                type: object
                properties:
                  slug:
                    type: string
                  status:
                    type: string
                    enum: [archived]
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ─── Batch B: Observability ───────────────────────────────────────────

  /v1/observability/destinations:
    get:
      operationId: listObservabilityDestinations
      summary: List broadcast destinations
      responses:
        "200":
          description: Destinations list
          content:
            application/json:
              schema:
                type: object
                properties:
                  enabled:
                    type: boolean
                  destinations:
                    type: array
                    items:
                      $ref: "#/components/schemas/ObservabilityDestination"
    post:
      operationId: addObservabilityDestination
      summary: Add a broadcast destination
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, type, config]
              properties:
                name:
                  type: string
                type:
                  type: string
                  enum: [webhook, otlp, datadog, splunk, posthog, s3, custom]
                config:
                  type: object
                event_types:
                  type: array
                  items:
                    type: string
                batch:
                  type: object
                  properties:
                    max_size:
                      type: integer
                    flush_interval_ms:
                      type: integer
      responses:
        "201":
          description: Destination added
          content:
            application/json:
              schema:
                type: object
                properties:
                  destination:
                    $ref: "#/components/schemas/ObservabilityDestination"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/observability/destinations/{id}:
    put:
      operationId: updateObservabilityDestination
      summary: Update a destination
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                enabled:
                  type: boolean
                event_types:
                  type: array
                  items:
                    type: string
                config:
                  type: object
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  destination:
                    $ref: "#/components/schemas/ObservabilityDestination"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    delete:
      operationId: deleteObservabilityDestination
      summary: Remove a destination
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Removed
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  status:
                    type: string

  /v1/observability/xdr/destinations:
    get:
      operationId: listXdrDestinations
      summary: List per-tenant XDR/SIEM destinations
      description: |
        Returns the tenant's configured XDR destinations. Secrets (apiKey,
        clientSecret, hecToken, workspaceKey, secretAccessKey, snake-case
        variants) are redacted in responses.
      responses:
        "200":
          description: Destinations
          content:
            application/json:
              schema:
                type: object
                properties:
                  destinations:
                    type: array
                    items:
                      $ref: "#/components/schemas/XdrDestination"
    post:
      operationId: createXdrDestination
      summary: Create an XDR destination
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, type, config]
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 128
                type:
                  type: string
                  enum: [crowdstrike, sentinel, splunk-ocsf, cortex, datadog-ocsf]
                config:
                  type: object
                  description: Adapter-specific config. Secret fields stored encrypted.
                event_types:
                  type: array
                  items:
                    type: string
                  description: |
                    Optional explicit event-type allowlist. Omit (or pass empty
                    array) to subscribe to all events not excluded by
                    `filter_rules`.
                filter_rules:
                  $ref: "#/components/schemas/DestinationFilterRules"
                batch:
                  $ref: "#/components/schemas/DestinationBatchConfig"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/XdrDestination"

  /v1/observability/xdr/destinations/{id}:
    put:
      operationId: updateXdrDestination
      summary: Update an XDR destination
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  minLength: 1
                  maxLength: 128
                enabled:
                  type: boolean
                config:
                  type: object
                event_types:
                  type: array
                  items:
                    type: string
                filter_rules:
                  oneOf:
                    - $ref: "#/components/schemas/DestinationFilterRules"
                    - type: "null"
                  description: Set null to remove existing filter rules.
                batch:
                  oneOf:
                    - $ref: "#/components/schemas/DestinationBatchConfig"
                    - type: "null"
                  description: Set null to remove existing batch policy.
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/XdrDestination"
    delete:
      operationId: deleteXdrDestination
      summary: Delete an XDR destination
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "204":
          description: Deleted

  /v1/observability/xdr/destinations/{id}/test:
    post:
      operationId: testXdrDestination
      summary: Send 1 synthetic OCSF event to verify destination connectivity
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Test result
          content:
            application/json:
              schema:
                type: object
                properties:
                  delivered:
                    type: boolean
                  error:
                    type: string
                  latency_ms:
                    type: number

  /v1/observability/xdr/destinations/{id}/stats:
    get:
      operationId: getXdrDestinationStats
      summary: Get last-24h delivery stats
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Stats
          content:
            application/json:
              schema:
                type: object
                properties:
                  sent:
                    type: integer
                  failed:
                    type: integer
                  dropped:
                    type: integer
                  last_delivery_at:
                    type: string
                    format: date-time
                    nullable: true

  /v1/observability/xdr/destinations/{id}/enable:
    post:
      operationId: setXdrDestinationEnabled
      summary: Enable or disable an XDR destination without deleting it
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [enabled]
              properties:
                enabled:
                  type: boolean
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/XdrDestination"

  /v1/admin/xdr/risk-push:
    post:
      operationId: pushXdrRisk
      summary: Push an external XDR risk score to feed the trust envelope
      description: |
        Stores `risk_score` at `xdr:risk:{tenantId}:{agentId}` in Redis with
        the supplied TTL (handler default when absent: 86400s / 24h; max 30d).
        The next trust envelope synthesized for that agent reads the score
        into `br_trust.xdr_risk`. Score >= 0.7 force-downgrades the agent to
        `restricted`-equivalent in routing.

        **Auth:** Platform admin only — RBAC requires the `platform.admin`
        permission (same gate as all other `/v1/admin/*` routes).

        **Feature flag:** This endpoint returns `403 feature_disabled` unless
        the `XDR_BIDIRECTIONAL_ENABLED=1` environment variable is set on the
        gateway task. The default is OFF.
      parameters:
        - name: dry_run
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: |
            When `true`, the score is **not** persisted. The response echoes
            what would have been written under `would_set`. Useful for
            verifying connectivity and payload shape from a partner XDR.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [tenant_id, agent_id, risk_score, source]
              properties:
                tenant_id:
                  type: string
                  format: uuid
                agent_id:
                  type: string
                  format: uuid
                risk_score:
                  type: number
                  minimum: 0
                  maximum: 1
                source:
                  type: string
                  minLength: 1
                  maxLength: 128
                  description: |
                    Required. Free-form source identifier (e.g.
                    "crowdstrike-falcon", "sentinel"). Logged on every push
                    for audit attribution.
                ttl_seconds:
                  type: integer
                  minimum: 1
                  maximum: 2592000
                  description: |
                    Optional. Score TTL in seconds. Handler default when
                    omitted is 86400 (24h). Max is 2592000 (30d).
      responses:
        "200":
          description: Pushed (or dry-run echo)
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  agent_id:
                    type: string
                    format: uuid
                  tenant_id:
                    type: string
                    format: uuid
                  risk_score:
                    type: number
                  source:
                    type: string
                  ttl_seconds:
                    type: integer
                  set_at:
                    type: string
                    format: date-time
                    description: Present in non-dry-run responses.
                  dry_run:
                    type: boolean
                    description: Present and `true` in dry-run responses.
                  would_set:
                    type: object
                    description: Present in dry-run responses; echoes the payload that would have been written.
        "403":
          description: Feature flag disabled (`XDR_BIDIRECTIONAL_ENABLED != "1"`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    delete:
      operationId: clearXdrRisk
      summary: Clear an existing XDR risk score before its TTL expires
      description: |
        Deletes the score at `xdr:risk:{tenantId}:{agentId}`. Useful when
        your XDR resolves an incident before the score's natural TTL
        expires. Same auth + feature flag as the POST variant.
      parameters:
        - name: dry_run
          in: query
          required: false
          schema:
            type: boolean
            default: false
          description: When `true`, no DELETE is performed; response echoes `would_delete`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [tenant_id, agent_id]
              properties:
                tenant_id:
                  type: string
                  format: uuid
                agent_id:
                  type: string
                  format: uuid
      responses:
        "200":
          description: Cleared (or dry-run echo)
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  agent_id:
                    type: string
                    format: uuid
                  tenant_id:
                    type: string
                    format: uuid
                  dry_run:
                    type: boolean
                  would_delete:
                    type: object
        "403":
          description: Feature flag disabled (`XDR_BIDIRECTIONAL_ENABLED != "1"`).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/observability/test/{id}:
    post:
      operationId: testObservabilityDestination
      summary: Send a test event to a destination
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Test event sent
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  event:
                    type: object
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "502":
          description: Destination unreachable
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/observability/config:
    get:
      operationId: getObservabilityConfig
      summary: Get observability config
      responses:
        "200":
          description: Config
          content:
            application/json:
              schema:
                type: object
                properties:
                  config:
                    type: object
                    properties:
                      enabled:
                        type: boolean
                      destinations:
                        type: array
                        items:
                          $ref: "#/components/schemas/ObservabilityDestination"
    put:
      operationId: setObservabilityConfig
      summary: Enable or disable observability
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [enabled]
              properties:
                enabled:
                  type: boolean
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  enabled:
                    type: boolean
                  destinations:
                    type: integer

  /v1/observability/templates:
    get:
      operationId: listObservabilityTemplates
      summary: List integration templates
      responses:
        "200":
          description: Template names
          content:
            application/json:
              schema:
                type: object
                properties:
                  templates:
                    type: array
                    items:
                      type: string

  /v1/observability/templates/{provider}:
    get:
      operationId: getObservabilityTemplate
      summary: Get integration template
      parameters:
        - name: provider
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Template
          content:
            application/json:
              schema:
                type: object
                properties:
                  template:
                    type: object
        "404":
          description: Unknown provider
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ─── Batch B: Governance ──────────────────────────────────────────────

  /v1/governance/memory/audit:
    get:
      operationId: getMemoryAudit
      summary: Memory change history
      parameters:
        - name: memory_key
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            default: 100
        - name: since
          in: query
          schema:
            type: number
            description: Unix epoch ms
        - name: operation
          in: query
          schema:
            type: string
            enum: [append, replace, delete, evict, pin]
      responses:
        "200":
          description: Audit entries
          content:
            application/json:
              schema:
                type: object
                properties:
                  entries:
                    type: array
                    items:
                      $ref: "#/components/schemas/AuditEntry"
                  count:
                    type: integer

  /v1/governance/memory/stats:
    get:
      operationId: getMemoryAuditStats
      summary: Memory operation statistics
      parameters:
        - name: memory_key
          in: query
          schema:
            type: string
        - name: since
          in: query
          schema:
            type: number
      responses:
        "200":
          description: Stats
          content:
            application/json:
              schema:
                type: object
                properties:
                  memory_key:
                    type: string
                  stats:
                    type: object
                    additionalProperties:
                      type: integer

  /v1/governance/memory/compliance:
    get:
      operationId: getMemoryCompliance
      summary: PII compliance scan
      description: Scan memory for PII violations (email, phone, SSN, credit card, API keys).
      parameters:
        - name: memory_key
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Compliance report
          content:
            application/json:
              schema:
                type: object
                properties:
                  total_entries:
                    type: integer
                  violations_found:
                    type: integer
                  compliant:
                    type: boolean
                  violations:
                    type: array
                    items:
                      type: object
                      properties:
                        entry_id:
                          type: string
                        block:
                          type: string
                        fact_preview:
                          type: string
                        issues:
                          type: array
                          items:
                            type: string

  /v1/governance/sleep-time/report:
    get:
      operationId: getSleepTimeReport
      summary: Sleep-time refinement history
      parameters:
        - name: memory_key
          in: query
          schema:
            type: string
        - name: since
          in: query
          schema:
            type: number
      responses:
        "200":
          description: Refinement report
          content:
            application/json:
              schema:
                type: object
                properties:
                  period_since:
                    type: string
                    format: date-time
                  total_refinements:
                    type: integer
                  consolidations:
                    type: integer
                  extractions:
                    type: integer
                  evictions:
                    type: integer

  /v1/governance/summary:
    get:
      operationId: getGovernanceSummary
      summary: Executive governance dashboard
      description: Memory health, PII compliance, and 24h activity.
      parameters:
        - name: memory_key
          in: query
          schema:
            type: string
      responses:
        "200":
          description: Governance summary
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GovernanceSummary"

  /v1/governance/completion-audit:
    get:
      operationId: getCompletionAudit
      summary: Completion audit trail
      description: Per-request identity and outcome audit log.
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
        - name: principal_id
          in: query
          schema:
            type: string
        - name: since
          in: query
          schema:
            type: string
            format: date-time
      responses:
        "200":
          description: Audit entries
          content:
            application/json:
              schema:
                type: object
                properties:
                  entries:
                    type: array
                    items:
                      type: object
                  count:
                    type: integer

  # ─── Batch B: Config ──────────────────────────────────────────────────

  /v1/config:
    get:
      operationId: listConfig
      summary: List config sections
      description: Returns all config sections the caller has permission to read.
      responses:
        "200":
          description: Config data
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    additionalProperties:
                      $ref: "#/components/schemas/ConfigSection"
        "503":
          description: Config service not available
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/config/{key}:
    get:
      operationId: getConfigSection
      summary: Get a config section
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Config section
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConfigSection"
        "403":
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Unknown key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    put:
      operationId: setConfigSection
      summary: Set a config section
      description: Full replace. Supports optimistic concurrency via If-Match header.
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
        - name: If-Match
          in: header
          schema:
            type: integer
          description: Expected version for optimistic concurrency
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  version:
                    type: integer
                  warnings:
                    type: array
                    items:
                      type: string
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Version conflict
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    patch:
      operationId: patchConfigSection
      summary: Merge-patch a config section
      description: RFC 7396 merge-patch. Null values remove keys.
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
        - name: If-Match
          in: header
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/merge-patch+json:
            schema:
              type: object
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  version:
                    type: integer
                  warnings:
                    type: array
                    items:
                      type: string
        "409":
          description: Version conflict
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    delete:
      operationId: resetConfigSection
      summary: Reset config to defaults
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean
                  key:
                    type: string

  /v1/config/{key}/audit:
    get:
      operationId: getConfigAudit
      summary: Config change audit trail
      parameters:
        - name: key
          in: path
          required: true
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
      responses:
        "200":
          description: Audit entries
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        event:
                          type: string
                        configPath:
                          type: string
                        actorId:
                          type: string
                        createdAt:
                          type: string
                          format: date-time

  # ─── Batch C: Ops ─────────────────────────────────────────────────────

  /v1/ops/status:
    get:
      operationId: getOpsStatus
      summary: AI operations center
      description: Single-pane-of-glass view across endpoints, budget, usage.
      responses:
        "200":
          description: Ops status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OpsStatus"

  # ─── Batch C: Leaderboard ─────────────────────────────────────────────

  /v1/models/leaderboard:
    get:
      operationId: getModelLeaderboard
      summary: Model performance leaderboard
      description: Rankings from Thompson sampling, live metrics, and pricing. Cached 5 min.
      parameters:
        - name: sort
          in: query
          schema:
            type: string
            enum: [overall, quality, speed, value, reliability]
            default: overall
        - name: provider
          in: query
          schema:
            type: string
        - name: limit
          in: query
          schema:
            type: integer
            default: 100
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: Leaderboard
          content:
            application/json:
              schema:
                type: object
                properties:
                  object:
                    type: string
                    enum: [list]
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/LeaderboardEntry"
                  sort_by:
                    type: string
                  cached_at:
                    type: string
                    format: date-time
                  model_count:
                    type: integer

  # ─── Batch C: Catalog ─────────────────────────────────────────────────

  /v1/catalog/refresh:
    post:
      operationId: refreshCatalog
      summary: Trigger model discovery
      responses:
        "200":
          description: Discovery events
          content:
            application/json:
              schema:
                type: object
                properties:
                  events:
                    type: array
                    items:
                      type: object
                  count:
                    type: integer

  /v1/catalog/status:
    get:
      operationId: getCatalogStatus
      summary: Catalog discovery status
      responses:
        "200":
          description: Status snapshot
          content:
            application/json:
              schema:
                type: object
                properties:
                  discovery:
                    type: object
                    properties:
                      running:
                        type: boolean
                  registry:
                    type: object
                    properties:
                      totalEndpoints:
                        type: integer
                      totalModels:
                        type: integer
                      providers:
                        type: object
                        additionalProperties:
                          type: integer

  # ─── Batch C: Replays ─────────────────────────────────────────────────

  /v1/replays/decisions:
    get:
      operationId: getReplayDecisions
      summary: List routing decisions
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            default: 100
        - name: since
          in: query
          schema:
            type: string
            format: date-time
      responses:
        "200":
          description: Decision log
          content:
            application/json:
              schema:
                type: object
                properties:
                  decisions:
                    type: array
                    items:
                      type: object
                      properties:
                        requestId:
                          type: string
                        timestamp:
                          type: string
                          format: date-time
                        model:
                          type: string
                        provider:
                          type: string
                        latencyMs:
                          type: number
                        costUsd:
                          type: number
                  count:
                    type: integer

  /v1/replays/simulate:
    post:
      operationId: simulateStrategy
      summary: Simulate alternative routing
      description: Run a different strategy against historical traffic to compare costs.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [strategy]
              properties:
                strategy:
                  type: string
                  enum: [price, latency, throughput, quality, priority, cascade]
                since:
                  type: string
                  format: date-time
                until:
                  type: string
                  format: date-time
      responses:
        "200":
          description: Simulation result
          content:
            application/json:
              schema:
                type: object
                properties:
                  actual:
                    type: object
                    properties:
                      total_requests:
                        type: integer
                      total_cost_usd:
                        type: number
                  simulated:
                    type: object
                    properties:
                      strategy:
                        type: string
                      total_cost_usd:
                        type: number
                      cost_savings_pct:
                        type: number

  /v1/replays/compare:
    get:
      operationId: compareStrategies
      summary: Compare all strategies
      parameters:
        - name: since
          in: query
          schema:
            type: string
            format: date-time
      responses:
        "200":
          description: Strategy comparison
          content:
            application/json:
              schema:
                type: object
                properties:
                  strategies:
                    type: array
                    items:
                      type: object
                      properties:
                        strategy:
                          type: string
                        total_cost_usd:
                          type: number
                        avg_latency_ms:
                          type: number

  # ─── Batch C: Capacity ────────────────────────────────────────────────

  /v1/capacity/forecast:
    get:
      operationId: getCapacityForecast
      summary: Capacity exhaustion forecast
      description: Predict when rate limits or budgets will be exhausted.
      responses:
        "200":
          description: Forecasts
          content:
            application/json:
              schema:
                type: object
                properties:
                  forecasts:
                    type: array
                    items:
                      type: object
                      properties:
                        provider:
                          type: string
                        avg_requests_per_hour:
                          type: number
                        daily_cost_usd:
                          type: number
                        trend:
                          type: string
                          enum: [increasing, stable, decreasing]

  /v1/capacity/providers:
    get:
      operationId: getCapacityProviders
      summary: Per-provider capacity status
      responses:
        "200":
          description: Provider capacity
          content:
            application/json:
              schema:
                type: object
                properties:
                  providers:
                    type: array
                    items:
                      type: object
                      properties:
                        provider:
                          type: string
                        total_requests_24h:
                          type: integer
                        success_rate:
                          type: number
                        avg_latency_ms:
                          type: number
                        health:
                          type: string
                          enum: [healthy, degraded, unhealthy]

  # ─── Batch C: Tasks ───────────────────────────────────────────────────

  /v1/tasks:
    post:
      operationId: createTask
      summary: Schedule a background task
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, prompt, schedule]
              properties:
                name:
                  type: string
                prompt:
                  type: string
                  description: Agent prompt to execute
                schedule:
                  type: string
                  description: Cron expression (5-6 fields)
                model:
                  type: string
                conversation_id:
                  type: string
                description:
                  type: string
                timezone:
                  type: string
      responses:
        "201":
          description: Task created
          content:
            application/json:
              schema:
                type: object
                properties:
                  task:
                    $ref: "#/components/schemas/Task"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    get:
      operationId: listTasks
      summary: List scheduled tasks
      responses:
        "200":
          description: Task list
          content:
            application/json:
              schema:
                type: object
                properties:
                  tasks:
                    type: array
                    items:
                      $ref: "#/components/schemas/Task"
                  total:
                    type: integer

  /v1/tasks/{id}:
    delete:
      operationId: deleteTask
      summary: Cancel a scheduled task
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  id:
                    type: string
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ── Batch D: Agent Profiles ──────────────────────────────────────────

  /v1/agent/profiles:
    get:
      operationId: listAgentProfiles
      summary: List agent profiles
      description: List all agent profiles for the current tenant.
      responses:
        "200":
          description: Profile list
          content:
            application/json:
              schema:
                type: object
                properties:
                  profiles:
                    type: array
                    items:
                      $ref: "#/components/schemas/AgentProfile"
                  total:
                    type: integer
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: createAgentProfile
      summary: Create agent profile
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AgentProfileInput"
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  profile:
                    $ref: "#/components/schemas/AgentProfile"
        "400":
          description: Missing required field
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Agent profile already exists
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/agent/profiles/me:
    get:
      operationId: getOwnAgentProfile
      summary: Get own agent profile
      description: Retrieve the calling agent's own profile. Requires agent JWT or mTLS.
      responses:
        "200":
          description: Agent profile
          content:
            application/json:
              schema:
                type: object
                properties:
                  profile:
                    $ref: "#/components/schemas/AgentProfile"
        "401":
          description: Agent identity required
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: No profile for this agent
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/agent/profiles/{agentId}:
    get:
      operationId: getAgentProfile
      summary: Get agent profile by ID
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Agent profile
          content:
            application/json:
              schema:
                type: object
                properties:
                  profile:
                    $ref: "#/components/schemas/AgentProfile"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    patch:
      operationId: updateAgentProfile
      summary: Update agent profile
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                displayName:
                  type: string
                ownerId:
                  type: string
                costCenter:
                  type: string
                budgetDailyUsd:
                  type: number
                budgetMonthlyUsd:
                  type: number
                role:
                  type: string
                manifestId:
                  type: string
                metadata:
                  type: object
      responses:
        "200":
          description: Updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  profile:
                    $ref: "#/components/schemas/AgentProfile"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    delete:
      operationId: deleteAgentProfile
      summary: Delete agent profile
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  agentId:
                    type: string
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/agent/profiles/lifecycle/{agentId}:
    patch:
      operationId: transitionAgentLifecycle
      summary: Transition agent lifecycle state
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [state]
              properties:
                state:
                  type: string
                  enum: [provisioned, active, quarantined, suspended, terminated]
      responses:
        "200":
          description: Transitioned
          content:
            application/json:
              schema:
                type: object
                properties:
                  profile:
                    $ref: "#/components/schemas/AgentProfile"
        "400":
          description: Invalid state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "409":
          description: Invalid lifecycle transition
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ── Agent Bootstrap & Self-Awareness ─────────────────────────────────

  /v1/agent/bootstrap:
    post:
      operationId: bootstrapAgent
      summary: Bootstrap an agent — create profile + issue JWT
      description: >-
        Zero-human agent onboarding. Creates an agent profile, writes enforcement
        limits, and issues a 1-hour JWT in one call. Idempotent on agent_id —
        re-bootstrapping an existing active agent issues a fresh JWT.
        Requires API key with explicit role scopes.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [agent_id]
              properties:
                agent_id:
                  type: string
                  description: Unique agent identifier (3-64 chars, lowercase alphanumeric + hyphens)
                  pattern: "^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$"
                display_name:
                  type: string
                cost_center:
                  type: string
                  description: Cost allocation tag for usage reporting
                budget_daily_usd:
                  type: number
                  description: Daily budget cap in USD
                budget_monthly_usd:
                  type: number
                  description: Monthly budget cap in USD
                metadata:
                  type: object
                  additionalProperties: true
                  description: Arbitrary metadata (set can_delegate=true to enable delegation)
      responses:
        "201":
          description: Agent bootstrapped (new profile created)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentBootstrapResponse"
        "200":
          description: Agent already exists — fresh JWT issued (idempotent)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentBootstrapResponse"
        "400":
          description: Invalid agent_id format or body
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: API key lacks scopes, or agent is suspended/terminated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/agent/status:
    get:
      operationId: getAgentStatus
      summary: Full agent self-awareness
      description: >-
        Returns the calling agent's profile, enforced limits (with real-time spend),
        recent anomaly events, and governance state. Requires agent JWT or mTLS.
      responses:
        "200":
          description: Agent status
          content:
            application/json:
              schema:
                type: object
                properties:
                  profile:
                    $ref: "#/components/schemas/AgentProfile"
                  limits:
                    $ref: "#/components/schemas/AgentLimitsStatus"
                  anomaly:
                    type: object
                    properties:
                      recent_events:
                        type: array
                        items:
                          type: object
                  governance:
                    type: object
                    properties:
                      lifecycle_state:
                        type: string
                      manifest_id:
                        type: string
                        nullable: true
                      role:
                        type: string
        "401":
          description: Agent identity required (JWT or mTLS)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/agent/limits/me:
    get:
      operationId: getAgentLimitsMe
      summary: Agent's own budget and rate limit status
      description: Returns budget spent, remaining, and RPM for the calling agent.
      responses:
        "200":
          description: Agent limits
          content:
            application/json:
              schema:
                type: object
                properties:
                  limits:
                    $ref: "#/components/schemas/AgentLimitsStatus"
        "401":
          description: Agent identity required
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/agent/anomaly/me:
    get:
      operationId: getAgentAnomalyMe
      summary: Recent security events for calling agent
      description: Returns the 20 most recent anomaly/security events for the calling agent.
      responses:
        "200":
          description: Anomaly events
          content:
            application/json:
              schema:
                type: object
                properties:
                  events:
                    type: array
                    items:
                      type: object
        "401":
          description: Agent identity required
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ── Agent Delegation (Swarms) ──────────────────────────────────────────

  /v1/agent/delegate:
    post:
      operationId: delegateAgent
      summary: Create a sub-agent with sliced budget
      description: >-
        Parent agent creates a child agent with a portion of its budget.
        Idempotent on agent_id. Requires agent JWT auth. Parent must have
        metadata.can_delegate=true and a finite budget. Child's role must be
        equal or lower privilege than parent's.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [agent_id, budget_allocation_usd]
              properties:
                agent_id:
                  type: string
                  description: Unique child agent identifier
                  pattern: "^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$"
                requested_role:
                  type: string
                  default: agent
                  description: Role for the child (must be subset of parent's role)
                requested_name:
                  type: string
                budget_allocation_usd:
                  type: number
                  description: Budget to slice from parent's daily budget
                ttl_seconds:
                  type: integer
                  description: Optional time-to-live for the child agent
                metadata:
                  type: object
                  additionalProperties: true
      responses:
        "201":
          description: Child agent created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentBootstrapResponse"
        "200":
          description: Child already exists (idempotent retry)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentBootstrapResponse"
        "400":
          description: Invalid request or parent lacks budget
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "402":
          description: Insufficient budget for delegation
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Delegation not enabled, parent not active, or privilege escalation
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/agent/sub-agents:
    get:
      operationId: listSubAgents
      summary: List caller's delegated children
      description: Returns all non-terminated child agents (all lifecycle states except terminated) with budget, lifecycle state, and expiry.
      responses:
        "200":
          description: Sub-agent list
          content:
            application/json:
              schema:
                type: object
                properties:
                  sub_agents:
                    type: array
                    items:
                      type: object
                      properties:
                        agent_id:
                          type: string
                        display_name:
                          type: string
                          nullable: true
                        role:
                          type: string
                        budget_daily_usd:
                          type: number
                          nullable: true
                        lifecycle_state:
                          type: string
                        expires_at:
                          type: string
                          format: date-time
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                  total:
                    type: integer
        "403":
          description: Requires agent JWT
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/agent/sub-agents/{childAgentId}:
    delete:
      operationId: terminateSubAgent
      summary: Terminate child agent and reclaim budget
      description: Terminates a delegated child agent. Budget is reclaimed to the parent.
      parameters:
        - name: childAgentId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Child terminated and budget reclaimed
          content:
            application/json:
              schema:
                type: object
                required:
                  - ok
                  - terminated_agent_id
                  - budget_refunded_usd
                properties:
                  ok:
                    type: boolean
                  terminated_agent_id:
                    type: string
                  budget_refunded_usd:
                    type: number
                  already_terminated:
                    type: boolean
                    description: Present and true when the child was already in terminated state
        "403":
          description: Not the parent of this agent
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Child agent not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ── Agent Limits (Bulk Management) ─────────────────────────────────────

  /v1/agent-limits:
    get:
      operationId: getAgentLimits
      summary: Read current per-agent limits
      description: Returns the enforcement limits map for all agents in the tenant.
      responses:
        "200":
          description: Agent limits map
          content:
            application/json:
              schema:
                type: object
                properties:
                  agentLimits:
                    type: object
                    additionalProperties:
                      $ref: "#/components/schemas/AgentLimitEntry"
    put:
      operationId: replaceAgentLimits
      summary: Replace entire agent limits map
      description: Replaces all per-agent budget and rate limit enforcement entries.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties:
                $ref: "#/components/schemas/AgentLimitEntry"
      responses:
        "200":
          description: Updated limits
          content:
            application/json:
              schema:
                type: object
                properties:
                  agentLimits:
                    type: object
                    additionalProperties:
                      $ref: "#/components/schemas/AgentLimitEntry"
                  version:
                    type: integer
    patch:
      operationId: mergeAgentLimits
      summary: Merge agent limits (set null to delete)
      description: >-
        Merge-updates per-agent limits. Set a key's value to null to remove
        that agent's limits. Non-null values are merged into existing entries.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      responses:
        "200":
          description: Updated limits
          content:
            application/json:
              schema:
                type: object
                properties:
                  agentLimits:
                    type: object
                    additionalProperties:
                      $ref: "#/components/schemas/AgentLimitEntry"
                  version:
                    type: integer

  # ── Agent Certificate Exchange & Revocation ─────────────────────────────

  /v1/agent/auth/cert:
    post:
      operationId: exchangeAgentCert
      summary: Exchange CSR for mTLS certificate
      description: >-
        Submit a PKCS#10 CSR with Agent JWT auth to receive a short-lived
        (5 min) mTLS client certificate with embedded SPIFFE identity.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [csr_pem]
              properties:
                csr_pem:
                  type: string
                  description: PEM-encoded PKCS#10 certificate signing request
      responses:
        "200":
          description: Signed certificate
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentCertResponse"
        "400":
          description: Invalid CSR
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Agent JWT required
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/agent/auth/revoke:
    post:
      operationId: revokeAgentCert
      summary: Revoke agent certificate
      description: >-
        Revoke a specific certificate by serial number, or bulk-revoke all
        certificates for a tenant. Requires cert.revoke permission (admin/operator).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                serial_number:
                  type: string
                  description: Certificate serial number to revoke (single cert)
                tenant_id:
                  type: string
                  description: Tenant ID for bulk revocation (all certs)
                reason:
                  type: string
                  default: manual_revocation
      responses:
        "200":
          description: Certificate(s) revoked
          content:
            application/json:
              schema:
                type: object
                properties:
                  revoked:
                    type: boolean
                  serial_number:
                    type: string
                  revoked_count:
                    type: integer
                  revoked_at:
                    type: string
                    format: date-time
        "400":
          description: Must provide serial_number or tenant_id
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: cert.revoke permission required
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ── Usage Analytics ────────────────────────────────────────────────────

  /v1/usage/spend:
    get:
      operationId: getUsageSpend
      summary: Daily spend breakdown
      parameters:
        - name: period
          in: query
          schema:
            type: string
            enum: [day, week, month]
            default: month
      responses:
        "200":
          description: Spend data
          content:
            application/json:
              schema:
                type: object

  /v1/usage/models:
    get:
      operationId: getUsageByModel
      summary: Per-model spend and request breakdown
      parameters:
        - name: period
          in: query
          schema:
            type: string
            enum: [day, week, month]
            default: month
      responses:
        "200":
          description: Per-model usage data
          content:
            application/json:
              schema:
                type: object

  /v1/usage/by-cost-center:
    get:
      operationId: getUsageByCostCenter
      summary: Spend grouped by agent cost center
      parameters:
        - name: period
          in: query
          schema:
            type: string
            enum: [day, week, month]
            default: month
      responses:
        "200":
          description: Cost center breakdown
          content:
            application/json:
              schema:
                type: object

  /v1/usage/by-owner:
    get:
      operationId: getUsageByOwner
      summary: Spend grouped by agent owner
      parameters:
        - name: period
          in: query
          schema:
            type: string
            enum: [day, week, month]
            default: month
      responses:
        "200":
          description: Owner breakdown
          content:
            application/json:
              schema:
                type: object

  /v1/usage/{requestId}/feedback:
    post:
      operationId: submitFeedback
      summary: Submit quality feedback on a completion
      parameters:
        - name: requestId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                score:
                  type: number
                  minimum: 0
                  maximum: 1
                  description: Quality score between 0.0 (bad) and 1.0 (good). Maps directly to Thompson sampling reward signal.
                comment:
                  type: string
                  description: Optional free-text feedback.
      responses:
        "200":
          description: Feedback recorded
          content:
            application/json:
              schema:
                type: object

  # ── Governance (Agent Manifests, Behavioral Profiles, Anomaly) ─────────

  /v1/governance/agent-manifests:
    get:
      operationId: listAgentManifests
      summary: List agent manifests (capability declarations)
      responses:
        "200":
          description: Manifest list
          content:
            application/json:
              schema:
                type: object

  /v1/governance/agent-manifests/{agentId}:
    get:
      operationId: getAgentManifest
      summary: Read specific agent manifest
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Agent manifest
          content:
            application/json:
              schema:
                type: object
    put:
      operationId: upsertAgentManifest
      summary: Create or update agent manifest
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                capabilities:
                  type: array
                  items:
                    type: string
                permissions:
                  type: array
                  items:
                    type: string
                signatures:
                  type: object
      responses:
        "200":
          description: Manifest saved
          content:
            application/json:
              schema:
                type: object
    delete:
      operationId: deleteAgentManifest
      summary: Remove agent manifest
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Manifest deleted
          content:
            application/json:
              schema:
                type: object

  /v1/governance/behavioral-profiles:
    get:
      operationId: listBehavioralProfiles
      summary: List behavioral profiles (role-based anomaly baselines)
      responses:
        "200":
          description: Profile list
          content:
            application/json:
              schema:
                type: object

  /v1/governance/behavioral-profiles/{role}:
    put:
      operationId: upsertBehavioralProfile
      summary: Create or update behavioral profile for a role
      parameters:
        - name: role
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                baseline_metrics:
                  type: object
                thresholds:
                  type: object
      responses:
        "200":
          description: Profile saved
          content:
            application/json:
              schema:
                type: object
    delete:
      operationId: deleteBehavioralProfile
      summary: Remove behavioral profile
      parameters:
        - name: role
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Profile deleted
          content:
            application/json:
              schema:
                type: object

  /v1/governance/anomaly/scores:
    get:
      operationId: getAnomalyScores
      summary: Real-time anomaly scores for tenant agents
      responses:
        "200":
          description: Anomaly scores
          content:
            application/json:
              schema:
                type: object

  /v1/governance/anomaly/history:
    get:
      operationId: getAnomalyHistory
      summary: Historical anomaly events
      parameters:
        - name: agent_id
          in: query
          schema:
            type: string
        - name: since
          in: query
          schema:
            type: string
            format: date-time
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
      responses:
        "200":
          description: Anomaly history
          content:
            application/json:
              schema:
                type: object

  # ── MCP Gateway ─────────────────────────────────────────────────────

  /v1/mcp/connect:
    get:
      operationId: getMcpCapabilities
      summary: MCP server capability discovery
      description: Returns server name, version, transport type, and available tools.
      responses:
        "200":
          description: MCP server info
          content:
            application/json:
              schema:
                type: object
                properties:
                  name:
                    type: string
                  version:
                    type: string
                  transport:
                    type: string
                  tools:
                    type: array
                    items:
                      type: string
    post:
      operationId: handleMcpRequest
      summary: MCP protocol handler
      description: >-
        Handle MCP JSON-RPC 2.0 messages over Streamable HTTP transport.
        Stateless — each request creates a new server instance.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [jsonrpc, id, method]
              properties:
                jsonrpc:
                  type: string
                  enum: ["2.0"]
                id:
                  type: [string, integer]
                method:
                  type: string
                params:
                  type: object
      responses:
        "200":
          description: JSON-RPC response
          content:
            application/json:
              schema:
                type: object
                properties:
                  jsonrpc:
                    type: string
                  id:
                    type: [string, integer]
                  result:
                    type: object
                  error:
                    type: object
                    properties:
                      code:
                        type: integer
                      message:
                        type: string

  # ── Batch D: Agent Service Mesh ──────────────────────────────────────

  /v1/mesh/register:
    post:
      operationId: meshRegister
      summary: Register agent in service mesh
      description: Register an agent instance. Requires mTLS authentication.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [hostname, endpoint]
              properties:
                hostname:
                  type: string
                endpoint:
                  type: string
                capabilities:
                  type: array
                  items:
                    type: string
      responses:
        "201":
          description: Registered
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent:
                    $ref: "#/components/schemas/MeshAgent"
        "400":
          description: Missing fields
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: mTLS required
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/mesh/heartbeat:
    post:
      operationId: meshHeartbeat
      summary: Agent heartbeat
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [hostname]
              properties:
                hostname:
                  type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        "403":
          description: SPIFFE mismatch
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/mesh/deregister:
    post:
      operationId: meshDeregister
      summary: Deregister agent from mesh
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [hostname]
              properties:
                hostname:
                  type: string
      responses:
        "200":
          description: Deregistered
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        "403":
          description: SPIFFE mismatch
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/mesh/invoke/{hostname}:
    post:
      operationId: meshInvoke
      summary: Invoke remote agent via mesh
      description: >-
        Agent-to-agent invocation via the service mesh. Requires mTLS.
        Source SPIFFE ID and hop count are server-injected.
      parameters:
        - name: hostname
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/A2APayload"
      responses:
        "200":
          description: A2A response
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Invalid A2A payload
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: mTLS required
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/mesh/agents:
    get:
      operationId: listMeshAgents
      summary: List mesh agents
      responses:
        "200":
          description: Agent list
          content:
            application/json:
              schema:
                type: object
                properties:
                  agents:
                    type: array
                    items:
                      $ref: "#/components/schemas/MeshAgent"

  /v1/mesh/resolve/{hostname}:
    get:
      operationId: resolveMeshAgent
      summary: Resolve agent by hostname
      parameters:
        - name: hostname
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Agent details
          content:
            application/json:
              schema:
                type: object
                properties:
                  agent:
                    $ref: "#/components/schemas/MeshAgent"
        "404":
          description: Agent not found or offline
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/mesh/graph:
    get:
      operationId: getMeshGraph
      summary: Mesh topology graph
      responses:
        "200":
          description: Topology
          content:
            application/json:
              schema:
                type: object

  /v1/mesh/policies:
    get:
      operationId: listMeshPolicies
      summary: List mesh RBAC policies
      responses:
        "200":
          description: Policy list
          content:
            application/json:
              schema:
                type: object
                properties:
                  policies:
                    type: array
                    items:
                      $ref: "#/components/schemas/MeshPolicy"
    post:
      operationId: createMeshPolicy
      summary: Create mesh RBAC policy
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [sourcePattern, destinationPattern, effect]
              properties:
                sourcePattern:
                  type: string
                destinationPattern:
                  type: string
                effect:
                  type: string
                  enum: [allow, deny]
                priority:
                  type: integer
                description:
                  type: string
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  policy:
                    $ref: "#/components/schemas/MeshPolicy"
        "400":
          description: Missing required fields
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/mesh/policies/{id}:
    delete:
      operationId: deleteMeshPolicy
      summary: Delete mesh policy
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/mesh/check:
    post:
      operationId: checkMeshAccess
      summary: Check mesh access policy
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [sourceSpiffeId, destinationSpiffeIdOrHostname]
              properties:
                sourceSpiffeId:
                  type: string
                destinationSpiffeIdOrHostname:
                  type: string
      responses:
        "200":
          description: Access check result
          content:
            application/json:
              schema:
                type: object
                properties:
                  allowed:
                    type: boolean
                  reason:
                    type: string
                  matchedPolicy:
                    $ref: "#/components/schemas/MeshPolicy"

  /v1/mesh/profiles:
    get:
      operationId: listBehavioralProfiles
      summary: List behavioral profiles
      responses:
        "200":
          description: Profile list
          content:
            application/json:
              schema:
                type: object
                properties:
                  profiles:
                    type: array
                    items:
                      $ref: "#/components/schemas/BehavioralProfile"
    post:
      operationId: createBehavioralProfile
      summary: Create behavioral profile
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [role]
              properties:
                role:
                  type: string
                maxRps:
                  type: number
                maxPayloadBytes:
                  type: integer
                allowedDestinations:
                  type: array
                  items:
                    type: string
                maxFanOut:
                  type: integer
                maxRequestsPerWindow:
                  type: integer
                windowSeconds:
                  type: integer
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  profile:
                    $ref: "#/components/schemas/BehavioralProfile"

  /v1/mesh/profiles/{role}:
    delete:
      operationId: deleteBehavioralProfile
      summary: Delete behavioral profile
      parameters:
        - name: role
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /auth/mesh/forensics:
    get:
      operationId: listForensicSnapshots
      summary: List forensic snapshots
      description: Requires Supabase JWT authentication (not API key).
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Snapshot list
          content:
            application/json:
              schema:
                type: object
                properties:
                  snapshots:
                    type: array
                    items:
                      $ref: "#/components/schemas/ForensicSnapshot"

  /auth/mesh/forensics/{id}:
    get:
      operationId: getForensicSnapshot
      summary: Get forensic snapshot
      description: Requires Supabase JWT authentication (not API key).
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Snapshot
          content:
            application/json:
              schema:
                type: object
                properties:
                  snapshot:
                    $ref: "#/components/schemas/ForensicSnapshot"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/mesh/forensics:
    get:
      operationId: listForensicSnapshotsV1
      summary: List forensic snapshots (API key)
      description: Returns raw forensic snapshots for the tenant. Uses API key authentication (same as other /v1/mesh admin endpoints). Response shape differs from /auth/mesh/forensics — fields are not normalized.
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Snapshot list
          content:
            application/json:
              schema:
                type: object
                properties:
                  snapshots:
                    type: array
                    items:
                      $ref: "#/components/schemas/ForensicSnapshotRaw"

  /v1/mesh/forensics/{id}:
    get:
      operationId: getForensicSnapshotV1
      summary: Get forensic snapshot (API key)
      description: Returns a raw forensic snapshot by ID. Uses API key authentication. Response shape differs from /auth/mesh/forensics — fields are not normalized.
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Snapshot
          content:
            application/json:
              schema:
                type: object
                properties:
                  snapshot:
                    $ref: "#/components/schemas/ForensicSnapshotRaw"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ── Batch D: SCIM 2.0 Provisioning ──────────────────────────────────

  /scim/v2/ServiceProviderConfig:
    get:
      operationId: getScimServiceProviderConfig
      summary: SCIM service provider configuration
      security:
        - BearerAuth: []
      responses:
        "200":
          description: SCIM config
          content:
            application/scim+json:
              schema:
                type: object

  /scim/v2/Schemas:
    get:
      operationId: getScimSchemas
      summary: List SCIM resource schemas
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Schema list
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimListResponse"

  /scim/v2/ResourceTypes:
    get:
      operationId: getScimResourceTypes
      summary: List SCIM resource types
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Resource types
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimListResponse"

  /scim/v2/Users:
    get:
      operationId: listScimUsers
      summary: List SCIM users
      security:
        - BearerAuth: []
      parameters:
        - name: filter
          in: query
          schema:
            type: string
        - name: startIndex
          in: query
          schema:
            type: integer
            default: 1
        - name: count
          in: query
          schema:
            type: integer
            default: 100
      responses:
        "200":
          description: User list
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimListResponse"
    post:
      operationId: createScimUser
      summary: Create SCIM user
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/scim+json:
            schema:
              type: object
              properties:
                userName:
                  type: string
                displayName:
                  type: string
                externalId:
                  type: string
                active:
                  type: boolean
                emails:
                  type: array
                  items:
                    type: object
                    properties:
                      value:
                        type: string
                        format: email
                      primary:
                        type: boolean
      responses:
        "201":
          description: Created
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimUser"
        "400":
          description: Validation error
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimError"

  /scim/v2/Users/{id}:
    get:
      operationId: getScimUser
      summary: Get SCIM user
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: User
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimUser"
        "404":
          description: Not found
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimError"
    put:
      operationId: replaceScimUser
      summary: Replace SCIM user
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/scim+json:
            schema:
              type: object
              properties:
                userName:
                  type: string
                displayName:
                  type: string
                externalId:
                  type: string
                active:
                  type: boolean
                emails:
                  type: array
                  items:
                    type: object
                    properties:
                      value:
                        type: string
                      primary:
                        type: boolean
      responses:
        "200":
          description: Updated
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimUser"
        "404":
          description: Not found
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimError"
    patch:
      operationId: patchScimUser
      summary: Patch SCIM user
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/scim+json:
            schema:
              $ref: "#/components/schemas/ScimPatchOp"
      responses:
        "200":
          description: Updated
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimUser"
        "404":
          description: Not found
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimError"
    delete:
      operationId: deleteScimUser
      summary: Delete SCIM user (soft-delete)
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "204":
          description: Deleted
        "404":
          description: Not found
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimError"

  /scim/v2/Groups:
    get:
      operationId: listScimGroups
      summary: List SCIM groups
      security:
        - BearerAuth: []
      parameters:
        - name: filter
          in: query
          schema:
            type: string
        - name: startIndex
          in: query
          schema:
            type: integer
            default: 1
        - name: count
          in: query
          schema:
            type: integer
            default: 100
      responses:
        "200":
          description: Group list
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimListResponse"
    post:
      operationId: createScimGroup
      summary: Create SCIM group
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/scim+json:
            schema:
              type: object
              required: [displayName]
              properties:
                displayName:
                  type: string
                externalId:
                  type: string
                members:
                  type: array
                  items:
                    type: object
      responses:
        "201":
          description: Created
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimGroup"
        "400":
          description: Validation error
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimError"

  /scim/v2/Groups/{id}:
    get:
      operationId: getScimGroup
      summary: Get SCIM group
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Group
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimGroup"
        "404":
          description: Not found
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimError"
    put:
      operationId: replaceScimGroup
      summary: Replace SCIM group
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/scim+json:
            schema:
              type: object
              required: [displayName]
              properties:
                displayName:
                  type: string
                externalId:
                  type: string
      responses:
        "200":
          description: Updated
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimGroup"
        "404":
          description: Not found
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimError"
    patch:
      operationId: patchScimGroup
      summary: Patch SCIM group
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/scim+json:
            schema:
              $ref: "#/components/schemas/ScimPatchOp"
      responses:
        "200":
          description: Updated
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimGroup"
        "404":
          description: Not found
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimError"
    delete:
      operationId: deleteScimGroup
      summary: Delete SCIM group
      security:
        - BearerAuth: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "204":
          description: Deleted
        "404":
          description: Not found
          content:
            application/scim+json:
              schema:
                $ref: "#/components/schemas/ScimError"

  # ── Platform Admin Routes ──────────────────────────────────────────────

  /v1/admin/tenants:
    get:
      operationId: listAdminTenants
      summary: List tenants
      description: |
        List all tenants with optional status filter.
        Requires platform admin privileges (user ID allowlist).
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [pending, active, suspended, rejected]
          description: Filter by tenant status
      responses:
        "200":
          description: Tenant list
          content:
            application/json:
              schema:
                type: object
                properties:
                  tenants:
                    type: array
                    items:
                      $ref: "#/components/schemas/TenantSummary"
        "403":
          description: Not a platform admin
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/admin/tenants/{id}:
    get:
      operationId: getAdminTenant
      summary: Get tenant detail
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Tenant detail
          content:
            application/json:
              schema:
                type: object
                properties:
                  tenant:
                    $ref: "#/components/schemas/TenantDetail"
        "404":
          description: Tenant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/admin/tenants/{id}/approve:
    post:
      operationId: approveTenant
      summary: Approve a tenant
      description: |
        Set tenant status to "active". Emits tenant.approved event and sends welcome email.
        Supports `?dry_run=true` for Virtual C-Suite agents.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: dry_run
          in: query
          schema:
            type: boolean
          description: Preview action without executing
      responses:
        "200":
          description: Tenant approved (or dry_run result)
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/AdminActionResult"
                  - $ref: "#/components/schemas/DryRunResult"
        "404":
          description: Tenant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/admin/tenants/{id}/reject:
    post:
      operationId: rejectTenant
      summary: Reject a tenant
      description: |
        Set tenant status to "rejected". Emits tenant.rejected event and sends rejection email.
        Supports `?dry_run=true`.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: dry_run
          in: query
          schema:
            type: boolean
      responses:
        "200":
          description: Tenant rejected (or dry_run result)
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/AdminActionResult"
                  - $ref: "#/components/schemas/DryRunResult"
        "404":
          description: Tenant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/admin/tenants/{id}/suspend:
    post:
      operationId: suspendTenant
      summary: Suspend a tenant
      description: |
        Set tenant status to "suspended". Supports `?dry_run=true`.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: dry_run
          in: query
          schema:
            type: boolean
      responses:
        "200":
          description: Tenant suspended (or dry_run result)
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/AdminActionResult"
                  - $ref: "#/components/schemas/DryRunResult"
        "404":
          description: Tenant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/admin/invite-codes:
    get:
      operationId: listInviteCodes
      summary: List invite codes
      responses:
        "200":
          description: Invite code list
          content:
            application/json:
              schema:
                type: object
                properties:
                  invite_codes:
                    type: array
                    items:
                      $ref: "#/components/schemas/InviteCode"
    post:
      operationId: createInviteCode
      summary: Create an invite code
      description: |
        Create a new invite code for beta gate bypass. Supports `?dry_run=true`.
      parameters:
        - name: dry_run
          in: query
          schema:
            type: boolean
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                code:
                  type: string
                  description: Custom code string (auto-generated if omitted)
                max_uses:
                  type: integer
                  nullable: true
                  description: Maximum redemptions (null = unlimited)
                expires_at:
                  type: string
                  format: date-time
                  nullable: true
                  description: Expiration timestamp (null = never)
      responses:
        "201":
          description: Invite code created
          content:
            application/json:
              schema:
                type: object
                properties:
                  invite_code:
                    $ref: "#/components/schemas/InviteCode"
        "200":
          description: Dry run result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DryRunResult"

  /v1/admin/invite-codes/{id}:
    delete:
      operationId: deleteInviteCode
      summary: Revoke an invite code
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: dry_run
          in: query
          schema:
            type: boolean
      responses:
        "200":
          description: Invite code deleted (or dry_run result)
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean
                  id:
                    type: string
        "404":
          description: Invite code not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/admin/digest:
    get:
      operationId: getAdminDigest
      summary: Business summary digest
      description: |
        Aggregated business metrics: tenant counts by status, pending approvals,
        total spend (last 30 days).
      responses:
        "200":
          description: Business digest
          content:
            application/json:
              schema:
                type: object
                properties:
                  digest:
                    $ref: "#/components/schemas/BusinessDigest"

  # ── Identity Bridge ────────────────────────────────────────────────

  /v1/tenant/claim-link:
    post:
      operationId: generateClaimLink
      summary: Generate a claim link for dashboard account linking
      description: |
        Creates a claim token that an OAuth user can redeem to link their
        dashboard identity to this tenant. Requires admin scope.
      security:
        - BearerAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                role:
                  type: string
                  enum: [owner, admin, member]
                  default: member
                max_uses:
                  type: integer
                  default: 1
                expires_in_days:
                  type: integer
                  default: 30
      responses:
        "200":
          description: Claim link generated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClaimLinkResponse"
        "403":
          description: Requires admin scope

  /v1/auth/device/authorize:
    post:
      operationId: authorizeDevice
      summary: Start device authorization flow
      description: |
        Generates a short-lived device code (15 min) that a user enters in the
        dashboard to link their identity. Requires admin scope.
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Device authorization started
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeviceAuthorizeResponse"

  /v1/auth/device/token:
    post:
      operationId: pollDeviceToken
      summary: Poll device authorization status
      description: |
        Returns `authorization_pending` (400) until the user confirms,
        `expired_token` (400) if the code expired, or `linked` (200) on success.
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [device_code]
              properties:
                device_code:
                  type: string
      responses:
        "200":
          description: Device linked successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum: [linked]
                  tenant:
                    type: object
                    properties:
                      id:
                        type: string
                      slug:
                        type: string
                  membership_id:
                    type: string
        "400":
          description: Pending, expired, or slow_down

  /v1/auth/link:
    post:
      operationId: initiateLink
      summary: Generate a browser-based link URL
      description: |
        Creates a one-time-use claim URL (7-day expiry) for browser-based linking.
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Link URL generated
          content:
            application/json:
              schema:
                type: object
                properties:
                  link_url:
                    type: string
                  expires_at:
                    type: string
                    format: date-time

  /auth/claim:
    post:
      operationId: claimTenant
      summary: Link OAuth identity to a tenant via claim token
      description: |
        Redeems a claim token, creating a per-tenant actor user and membership.
        The calling identity must be authenticated via Supabase JWT.
      security:
        - SupabaseJWT: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [claim_token]
              properties:
                claim_token:
                  type: string
                  description: "Claim token (brct_... prefix)"
      responses:
        "200":
          description: Tenant claimed successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClaimResult"
        "400":
          description: Invalid or expired token
        "409":
          description: Already linked to this tenant

  /auth/device/confirm:
    post:
      operationId: confirmDeviceCode
      summary: Confirm a device code from the browser
      description: |
        Browser-side confirmation of a device flow. The user enters the
        `user_code` displayed in their terminal.
      security:
        - SupabaseJWT: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [user_code]
              properties:
                user_code:
                  type: string
                  description: "Device code (BRSR-XXXXXX format)"
      responses:
        "200":
          description: Device linked
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  tenant_id:
                    type: string
                  role:
                    type: string
        "400":
          description: Invalid or expired device code
        "409":
          description: Already linked to this tenant

  # ── Agent Runs API (TS-1 parity fix 2026-05-09) ──────────────────────────
  /v1/agents/{agentId}/run:
    post:
      operationId: runAgent
      summary: Run agent loop (SSE stream)
      description: |
        Starts the agent loop for a given agent. Creates a new session (or reuses
        `session_id` if provided), runs the loop until completion, and streams
        lifecycle events as Server-Sent Events. The stream closes when the loop ends
        (idle, max_turns, budget, timeout, or error).
      tags: [Agents]
      security:
        - BearerAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [message]
              properties:
                message:
                  type: string
                  maxLength: 100000
                  description: User message to kick off the loop.
                session_id:
                  type: string
                  format: uuid
                  description: Reuse an existing session. Auto-created if omitted.
                max_turns:
                  type: integer
                  minimum: 1
                  maximum: 100
                max_duration_ms:
                  type: integer
                  minimum: 1000
                  maximum: 3600000
                budget_cap_usd:
                  type: number
                  minimum: 0
                system_prompt:
                  type: string
                  maxLength: 50000
      responses:
        "200":
          description: SSE stream of loop lifecycle events.
          content:
            text/event-stream:
              schema:
                type: string
        "404":
          description: Agent not found.
        "503":
          description: Database not configured.

  /v1/agents/{agentId}/sessions:
    post:
      operationId: createAgentSession
      summary: Create a session (no run)
      description: Creates a persistent session context for an agent without starting a run.
      tags: [Agents]
      security:
        - BearerAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                session_key:
                  type: string
                  maxLength: 256
                metadata:
                  type: object
      responses:
        "201":
          description: Session created.
          content:
            application/json:
              schema:
                type: object
                properties:
                  session_id:
                    type: string
                    format: uuid
                  agent_id:
                    type: string
                  tenant_id:
                    type: string

  /v1/agents/{agentId}/sessions/{sessionId}:
    get:
      operationId: getAgentSession
      summary: Session status and metadata
      tags: [Agents]
      security:
        - BearerAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Session details.
          content:
            application/json:
              schema:
                type: object
                properties:
                  session_id:
                    type: string
                  status:
                    type: string
        "404":
          description: Session not found.
    delete:
      operationId: terminateAgentSession
      summary: Terminate a session
      tags: [Agents]
      security:
        - BearerAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Session terminated.
        "404":
          description: Session not found.

  /v1/agents/{agentId}/sessions/{sessionId}/events:
    post:
      operationId: sendAgentSessionEvent
      summary: Send an event to a running session
      description: Inject user messages or interrupt signals into a live agent loop.
      tags: [Agents]
      security:
        - BearerAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [type]
              properties:
                type:
                  type: string
                  enum: [user.message, user.interrupt]
                text:
                  type: string
                  maxLength: 100000
                reason:
                  type: string
                  maxLength: 1000
      responses:
        "200":
          description: Event accepted.
        "404":
          description: Session not found.

  /v1/agents/{agentId}/runs:
    get:
      operationId: listAgentRuns
      summary: Run history for an agent
      tags: [Agents]
      security:
        - BearerAuth: []
      parameters:
        - name: agentId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
      responses:
        "200":
          description: List of past runs.
          content:
            application/json:
              schema:
                type: object
                properties:
                  runs:
                    type: array
                    items:
                      type: object
                  total:
                    type: integer

  /v1/runs/active:
    get:
      operationId: listActiveRuns
      summary: All active agent runs for the tenant
      tags: [Agents]
      security:
        - BearerAuth: []
      responses:
        "200":
          description: Active run list.
          content:
            application/json:
              schema:
                type: object
                properties:
                  runs:
                    type: array
                    items:
                      type: object
